<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://jeffburg.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://jeffburg.com/" rel="alternate" type="text/html" /><updated>2026-07-28T10:48:18+00:00</updated><id>https://jeffburg.com/feed.xml</id><title type="html">Jeffrey Bergier</title><entry><title type="html">ENIL: LINE Messenger for Retro Devices</title><link href="https://jeffburg.com/apps/retro-tech/unenshittification/2026/06/22/LINE-Messenger-for-Retro-Devices.html" rel="alternate" type="text/html" title="ENIL: LINE Messenger for Retro Devices" /><published>2026-06-22T00:00:00+00:00</published><updated>2026-06-22T00:00:00+00:00</updated><id>https://jeffburg.com/apps/retro-tech/unenshittification/2026/06/22/LINE-Messenger-for-Retro-Devices</id><content type="html" xml:base="https://jeffburg.com/apps/retro-tech/unenshittification/2026/06/22/LINE-Messenger-for-Retro-Devices.html"><![CDATA[<video muted="" playsinline="" controls="" preload="metadata" poster="/assets/images/unenshittification/enil/enil-demonstration-poster.png" aria-label="ENIL exchanging LINE messages between Mac OS X and iOS" style="display: block; width: 100%; height: auto;">
  <source src="/assets/images/unenshittification/enil/enil-demonstration.mp4" type="video/mp4" />
  Your browser does not support embedded MP4 video.
</video>

<h2 id="what-is-enil">What is ENIL</h2>

<p>ENIL is my custom-built LINE companion client for retro Apple devices. It brings
the core messaging experience to systems that the official LINE apps no longer
support. The Mac app targets Mac OS X 10.4 Tiger and later, with PowerPC, 32-bit
Intel, 64-bit Intel, and Apple silicon slices in one
<a href="https://en.wikipedia.org/wiki/Universal_binary">universal binary</a>. The iOS app
targets iOS 4.3 and later and includes armv7 and arm64 slices.
<i class="fa-regular fa-face-grin-tongue-squint"></i></p>

<p>Not having a working LINE application was a real hurdle to
<a href="/unenshittification/2026/02/22/Unenshittification-101.html">using my iPhone 5 running iOS 6 as my primary iPhone</a>.
While the old LINE app still exists for iOS 6, it no longer does anything, not
even present an error. I live in Japan, where LINE Messenger is a huge part of
daily life. And it was not only my iPhone: I also have a 12-inch Retina MacBook
Adorable that I use every day. This computer runs Mojave because it is extremely
fast, but the official LINE Mac application no longer works there either. The
official clients no longer support either system, which left a significant gap
in my daily setup.</p>

<p>So I built ENIL so I can use LINE to stay in touch with my friends on my Retro
Devices that LINE no longer supports. I have been using it daily for a few 
months now and I am extremely happy with how it has turned out.</p>

<h3 id="what-is-line-messenger">What is LINE Messenger</h3>

<p>If you have lived in Japan, Taiwan, or Thailand, then you probably know
<a href="https://en.wikipedia.org/wiki/Line_%28software%29">LINE Messenger</a>. It is a
standard way for people, restaurants, and other businesses to communicate. It
works similarly to WhatsApp or iMessage, and contacts can be added via QR code.
It has the usual features of a modern messaging app, including groups, voice
calls, and video calls. Most importantly for this project, it uses
<a href="https://help.line.me/line/pc?contentId=50000087&amp;lang=en">Letter Sealing</a>,
LINE’s branding for end-to-end encryption. Supporting Letter Sealing was
therefore essential for ENIL.</p>

<h3 id="enil-features">ENIL Features</h3>

<ul>
  <li>QR code login</li>
  <li>History sync</li>
  <li>Purchased Sticker and Emoji sync</li>
  <li>One-to-one and group chats</li>
  <li>Sending and receiving stickers, emoji, and photos</li>
  <li>Separate ‘Seen’ and ‘Read’ states</li>
  <li>A Mark as Seen button, rather than automatically marking a chat when I
open it</li>
  <li>Multiple accounts</li>
</ul>

<h3 id="caveats">Caveats</h3>

<ul>
  <li>ENIL depends on a Cloudflare Worker for cryptographic operations.</li>
  <li>It does not include every LINE feature. Major omissions include voice and
video calling and interactive features used by LINE Official Accounts.</li>
  <li>ENIL is an independent personal project and is not affiliated with, endorsed
by, or supported by LINE. Because it depends on an online service that changes
over time, future LINE updates may require corresponding ENIL updates.</li>
  <li>This is a niche app, mainly useful to LINE users who also enjoy retro
devices.</li>
  <li>ENIL is a companion client, not a replacement for LINE’s main mobile app. You
still need a working modern LINE device to scan and approve the login QR code
and to manage the account.</li>
  <li>I do not have an iPhone running iOS 4.3. ENIL should work on iOS 4.3, but I 
have not tested it.</li>
</ul>

<h3 id="how-can-you-get-it">How Can You Get It</h3>

<p>ENIL is still a personal project, so its repositories remain private for now,
but I hope to share more in the future. In the meantime, if you are interested
in testing ENIL, please contact me on Mastodon:</p>

<p><a href="https://jeffburg.social/@jeff">@jeff@jeffburg.social</a></p>

<h3 id="how-does-enil-work">How Does ENIL Work</h3>

<p>After you log in by scanning a QR code with LINE’s main mobile app, ENIL syncs
the available history, purchased stickers, and emoji. Each account keeps its
own session state and <a href="https://sqlite.org/about.html">SQLite</a> database. ENIL then
opens a
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events">Server-Sent Events (SSE)</a>
connection through which LINE delivers new messages, read state, and other
metadata. The client records the last operation revision so it can reconnect
from that point after a timeout or sleep; if LINE requests a full sync, ENIL
performs one again.</p>

<p>Account metadata, chat state, original message data, and downloaded asset paths
are stored in SQLite. When you open a chat, ENIL generates an HTML page in C and
loads it into a WebView. These WebViews do no networking because retro devices
have old TLS stacks that do not support TLS 1.1/1.2. Instead ENIL links
a custom build of <a href="https://curl.se/libcurl/">libcurl</a> provided by
<a href="/unenshittification/2026/04/22/Altivec-Intelligence.html">Altivec Intelligence</a>. The C
code in ENIL does all the networking, JSON parsing, and database storage
directly before handing anything off to Objective-C for showing in the UI.
Avatars, stickers, emoji, and other assets are first downloaded to disk and
then the Webviews render them from there.</p>

<p>When an SSE event arrives, ENIL writes it to the database and posts a
notification through
<a href="https://developer.apple.com/documentation/foundation/notificationcenter"><code class="language-plaintext highlighter-rouge">NSNotificationCenter</code></a>.
An open view controller can then append the new message to its WebView with
JavaScript. On systems that support local notifications, ENIL posts one for a
new message only while the app is in the background. On iOS, ENIL declares the
VoIP background mode and embeds the private unlimited-assertions entitlement, so
it can remain connected to LINE’s SSE stream while running in the background.</p>

<h3 id="encryption-and-letter-sealing">Encryption and Letter Sealing</h3>

<p>Messages protected by Letter Sealing arrive encrypted, whether fetched during
sync or received over SSE. Before displaying the message, ENIL sends the
ciphertext to the Cloudflare Worker and receives the decrypted content. When
sending a message, the Worker performs the reverse operation and ENIL sends the
resulting ciphertext directly to LINE. The need for the Cloudflare worker is
described later but I think it is unavoidable on retro devices for now. That
said, Cloudflare workers have no state and no storage, or at least I do not use
those features for this worker, so I think we can consider this to be secure.
But use at your own risk.</p>

<h3 id="ui-structure">UI Structure</h3>

<p>The iOS and Mac apps share the same basic structure. The chat and friends lists
are native table views. This was a bit tricky because Mac OS X did not get
view-based table views until 10.7, and I support 10.4. On Tiger, ENIL therefore
uses a custom <code class="language-plaintext highlighter-rouge">NSCell</code> which draws its contents manually with AppKit. This is
inconvenient, but it works.</p>

<p>Each individual chat is a WebView. When you receive a new message, JavaScript
appends it to the open WebView. Sticker and emoji pickers are also WebViews.
Some of the stickers and emojis use
<a href="https://en.wikipedia.org/wiki/APNG">animated PNG</a> files. If you are running
iOS 8 or OS X 10.10 Yosemite or later, then the WebView will animate these
automatically. On earlier systems they appear as static images.</p>

<p>One main Mac and iOS feature difference is that Mac OS X has supported
<code class="language-plaintext highlighter-rouge">NSAttributedString</code> and <code class="language-plaintext highlighter-rouge">NSTextAttachment</code> since before Mac OS X even existed.
Yes, even <a href="/apps/2025/10/06/MathEdit.html">OpenStep 4.2</a> had these features.
AND YET, iOS lacked them until around iOS 7. On the Mac, a custom emoji
therefore appears inline in the message text as you type. On iOS, I use a simple
Unicode placeholder; the custom emoji appears only after you send the message,
when the WebView can properly render the text and images inline on any platform.</p>

<h2 id="the-game">The Game</h2>

<p>LINE has the concept of a “companion app.” I have used the Mac version of this
for a long time. For a while I was thinking I would not even need to talk
directly to LINE because I could just read the data out of the Mac app’s
database. But it turned out there was also a Chrome extension companion app.</p>

<p>I was super excited when I learned this because browser extensions are just a
bunch of JavaScript. So yes, it will be minified and obfuscated, but all the
code is there! No need to decompile or sniff the network traffic. All of the
code is just sitting there!</p>

<h3 id="webassembly-the-final-boss">WebAssembly: The Final Boss</h3>

<p>So I downloaded the Chrome extension and asked the AI to format the
minified JavaScript and find the app’s key functionality. When its summary came
back, my heart sank a bit. These LINE companion apps rely on a large 3 MB
<a href="https://webassembly.org/">WebAssembly (Wasm)</a> binary blob to do much of their
work. When I first heard of WebAssembly, I was excited that people could ship
performant compiled code to the browser. What I had not considered was that now
vendors could ship “closed source” code to the browser. In other words, it is no
longer just JavaScript that can be easily read and evaluated. 
<i class="fa-regular fa-face-surprise"></i></p>

<p>At this point, I thought my journey was dead. There was no practical way to make
these retro systems run WebAssembly code. My <a href="/retro-tech/2025/08/17/Retro-Stream-Tutorial.html">iMac G4</a> 
shipped in 2003 and my <a href="/retro-tech/2026/02/16/Reverse-iTunes-1.html">iPhone 5</a> in
2012, years before WebAssembly even existed. Getting the binary to run on these
devices would easily exceed any amount of time I could commit to this project,
with or without AI.</p>

<h3 id="cloudflare-its-highly-effective">Cloudflare: It’s Highly Effective</h3>

<p>At some point, in the shower probably, I realized “Wait! I don’t need to run
WebAssembly on the device! I only need to run it in Cloudflare!” The Wasm blob
only did basic cryptographic operations, which meant I could put a simple JSON
interface in front of it and just use normal HTTPS requests to interact with it.</p>

<p>So I asked the AI to make me a simple
<a href="https://developers.cloudflare.com/workers/">Cloudflare Worker</a> that “booted up”
the Wasm blob and exposed its critical functions. The worker and the Wasm binary
handle a lot of misc cryptographic functionality such as HMAC operations, 
key generation, keychain unwrapping, and group-key operations. This enables
ENIL to interact with LINE’s very secure API with full Letter Sealing (E2E 
Encryption) functionality maintained.</p>

<p>From there, I had the AI cook up a little CLI-based JavaScript app that used the
LINE API as presented by the Chrome extension and the Cloudflare Worker to
successfully:</p>

<ol>
  <li>Log in to the “Chrome extension” by scanning a QR code with the LINE app on
my phone</li>
  <li>Download a list of recent chats</li>
  <li>View a message conversation</li>
  <li>Send a message</li>
</ol>

<p>This PoC was extremely important because it proved that I could implement a
compatible companion client and interact with the LINE API directly. My client
still needed the Cloudflare Worker for cryptographic operations, but it made
every LINE request itself. From LINE’s perspective, the requests were coming
from a client device running Chrome from a normal client IP address.</p>

<p><a href="/assets/images/unenshittification/enil/enil-cli-full.png"><img src="/assets/images/unenshittification/enil/enil-cli-thumb.png" alt="ENIL command-line proof of concept" /></a></p>

<h3 id="cocoa-level-up">Cocoa Level-Up</h3>

<p>So a stupid little JavaScript PoC was fine and all, but it was time to 1-Up this
bitch and get a real Mac and iPhone app working… for Mac OS X Tiger and iOS
4.3 <i class="fa-regular fa-face-laugh-squint"></i>. No but really, but this kind
of app is conceptually simple and should not require any modern APIs. Chat apps
have existed for Mac OS X and iOS for as long as both platforms have existed.
The only thing I would need is modern networking which is thankfully provided
by my bedrock development environment
<a href="/unenshittification/2026/04/22/Altivec-Intelligence.html">Altivec Intelligence</a>.
This includes all the basics like
modern OpenSSH, libcurl, and cJSON for modern networking. It also provides a
modern build of SQLite. With this basic bedrock I can build apps that interact
with Modern Web APIs, save the info to the database and display it in the UI.</p>

<h3 id="ai-power-up">AI Power-Up</h3>

<p>When I was originally thinking about
<a href="/unenshittification/2026/04/22/Altivec-Intelligence.html">Altivec Intelligence</a> and how I
would write code for retro devices, I thought, “No big deal. I need to use
libcurl, cJSON, and SQLite, but I can wrap them in little Objective-C wrappers
and then do normal Cocoa programming.” However, after I thought longer about it,
I realized that these wrappers provided by Apple are actually quite huge and
complex. They are nice but would I really want to rebuild them myself? Also, 
since old Apple platforms do not have NSJSONSerialization, it means I need a
C-based JSON parser. So that means that libcurl is in C, cJSON is in C, and
SQLite is in C… so I just couldn’t help but think “Why would I bridge back
and forth between C and Objective-C if all of these libraries are in C anyway?”</p>

<p>So I asked this question to the AI and it said it had no issues
writing everything in C… so that’s what we did. And it worked… like super
well. AI plus the combo of LLVM/GCC -wAll and
<a href="https://clang.llvm.org/docs/ClangStaticAnalyzer.html">LLVM static analysis</a>
meant that there was plenty to reason about in terms of the stability and
correctness of the code. In fact, it’s extremely stable and EXTREMELY fast. Yes
it’s also insanely verbose… but that’s ok, it’s being generated by a computer.</p>

<p>Basically, I came to the realization that just because my comfort zone is
Objective-C and Swift, it does not mean I need to do every task in those
languages. Instead, I could accept the AI’s help and start writing code outside
my comfort zone. I decided to use AI to help me write the C engine while keeping
the UI in Objective-C. I ended up with a shared iOS and Mac codebase that is
about 75% C and 25% Objective-C.</p>

<p>I will post more about this so stay tuned. But this is one of the great powers
of AI-assisted programming. Instead of trying to do everything in the language
you personally prefer, use the right language at the right time for the right
job. AI makes that jump less intimidating, but generated code still requires
planning, validation, and testing on real devices. Also, producing and
validating thousands of lines of C also took far more tokens than producing
something similar in JavaScript. At this point, I had to move from my $20 plan
to the $100 plan to support this use case without constantly hitting limits.</p>

<h2 id="enil-is-right-for-me">ENIL is Right for Me</h2>

<p>ENIL is my own personal app. It’s made by me, for me, and I use it every day
and I love it. It means I can
<a href="/unenshittification/2026/05/22/Unenshittification-Manifesto.html">use the devices I want to use</a>
when I want to use them and still stay in touch with my friends. It <del>proves</del>
shows that you don’t need an Apple silicon powerhouse chip with gobs of RAM and
a 300 MB binary to do simple things like chat apps do. It’s simple, it’s fast, and
it works, for me.</p>]]></content><author><name></name></author><category term="Apps" /><category term="Retro-Tech" /><category term="Unenshittification" /><category term="LINE" /><category term="PowerPC" /><category term="Mac-OS-X" /><category term="iOS" /><category term="Objective-C" /><category term="C" /><category term="AI" /><summary type="html"><![CDATA[ENIL is my custom-built LINE Messenger companion app, made for old and unsupported devices. It supports Mac OS X 10.4 Tiger and later, as well as iOS 4.3 and later.]]></summary></entry><entry><title type="html">Unenshittification Manifesto</title><link href="https://jeffburg.com/unenshittification/2026/05/22/Unenshittification-Manifesto.html" rel="alternate" type="text/html" title="Unenshittification Manifesto" /><published>2026-05-22T00:00:00+00:00</published><updated>2026-05-22T00:00:00+00:00</updated><id>https://jeffburg.com/unenshittification/2026/05/22/Unenshittification-Manifesto</id><content type="html" xml:base="https://jeffburg.com/unenshittification/2026/05/22/Unenshittification-Manifesto.html"><![CDATA[<h2 id="what-i-have-learned">What I have learned</h2>

<p>This manifesto grew out of about five months of “spare time” spent trying to
<a href="/unenshittification/2026/02/22/Unenshittification-101.html">unenshittify my life</a>. I now
have everyday apps running on ancient computers like my iPhone 5 and iMac G4.
That success has convinced me that, without enshittification, everyday
apps could offer better user experiences and run on nearly any computer built
since 2010.</p>

<p>What is an everyday app? A basic native application that fetches user data
from a web API, stores it locally, displays it in a UI, and lets the user
change and synchronize it. That describes every chat app, every doomscrolling
social media app, every “AI” app, and pretty much any other app except games
and apps for producing audio or video. I am pretty confident these apps account
for 99% of the time you spend on your phone.</p>

<h2 id="what-i-refuse">What I refuse</h2>

<ul>
  <li>I refuse to accept that everyday apps require modern hardware</li>
  <li>I refuse to accept that everyday apps require 300 MB of human-made slop</li>
  <li>I refuse to accept being denied access to my own data</li>
  <li>I refuse to accept digital experiences that are actively hostile to my goals</li>
  <li>I refuse to carry a 7-inch supercomputer merely to chat and doomscroll</li>
</ul>

<h2 id="what-i-believe">What I believe</h2>

<ul>
  <li>I believe that users should decide what code runs on their computers</li>
  <li>I believe that everyday apps can run GREAT on retro hardware</li>
  <li>I believe that a GREAT everyday app can fit in a 5 MB binary</li>
  <li>I believe that my data is my data and I can use it where and how I want</li>
  <li>I believe that GREAT experiences help me achieve my goals</li>
  <li>I believe that I can create my own GREAT experiences</li>
</ul>

<h2 id="what-i-accept">What I accept</h2>

<ul>
  <li>I accept that I cannot fix the modern app and web ecosystems</li>
  <li>I accept that I cannot force Apple to give users control of their own data</li>
  <li>I accept that the apps I create are not 100% feature-complete</li>
  <li>I accept that the apps I create are not 100% bug-free</li>
  <li>I accept that AI slop is neither beautiful nor pleasant for humans to read</li>
  <li>I accept that AI slop is not free</li>
</ul>

<h2 id="what-i-will-do">What I will do</h2>

<ul>
  <li>I will build my own apps for my own devices</li>
  <li>I will make my data accessible to my own apps</li>
  <li>I will use the devices I want to use</li>
  <li>I will make AI slop apps outperform human slop apps in speed, stability, and focus</li>
  <li>I will use AI to write MOST of my code</li>
  <li>I will compile ALL of my code</li>
</ul>

<h3 id="i-will-unenshittify-my-life-">I Will Unenshittify My Life <i class="fa-solid fa-hand-fist"></i></h3>]]></content><author><name></name></author><category term="Unenshittification" /><category term="Unenshittification" /><summary type="html"><![CDATA[A personal manifesto for taking back control of my digital life.]]></summary></entry><entry><title type="html">Altivec Intelligence</title><link href="https://jeffburg.com/unenshittification/2026/04/22/Altivec-Intelligence.html" rel="alternate" type="text/html" title="Altivec Intelligence" /><published>2026-04-22T00:00:00+00:00</published><updated>2026-04-22T00:00:00+00:00</updated><id>https://jeffburg.com/unenshittification/2026/04/22/Altivec-Intelligence</id><content type="html" xml:base="https://jeffburg.com/unenshittification/2026/04/22/Altivec-Intelligence.html"><![CDATA[<h2 id="lessons-learned">Lessons Learned</h2>

<p>In 2024 I developed MathEdit for OpenStep. This app was a proof of concept to
see if I could develop a Cocoa application for OpenStep and then upgrade it to
work on every version of Mac OS X. The application was a success, but even
though it was a simple app, it took me exactly one year of spare-time programming
to develop and then to upgrade through 20 years of Mac OS X history. The
primary reasons for this were that Objective-C from 1996 was pretty painful to
write, and I was writing it all in real OpenStep and vintage Mac OS X virtual
machines with ancient tooling.</p>

<p>In the age of AI, this solution was not going to suit me anymore. I needed
a modern environment that could run on any computer and still create apps for my
favorite retro platforms. I needed this platform to support AI because it
reduces the pain of writing Objective-C 1.0 code that lacks:</p>

<ul>
  <li>Automatic Reference Counting</li>
  <li><a href="https://fuckingblocksyntax.com/">Fucking Block Syntax</a></li>
  <li>Property Syntax</li>
  <li>Dot Syntax</li>
  <li>Grand Central Dispatch</li>
  <li>Collection Literals</li>
</ul>

<h2 id="altivec-intelligence">Altivec Intelligence</h2>

<p>Altivec Intelligence is an all-in-one integrated development Docker container
that has everything I need to develop Cocoa and Cocoa Touch applications that
run on <a href="/retro-tech/">PowerPC Macs</a> with Mac OS X Tiger or later and iPhones
running iOS 4.3 and later. And before you ask, yes, the Mac apps are universal
binaries with four slices: PowerPC, x86, x64, and arm64
<i class="fa-brands fa-hugging-face"></i>.</p>

<p>On top of that, Altivec Intelligence includes tooling for web development,
image manipulation, and FFmpeg. Most importantly, all of the AI agent CLIs are
baked into the container.</p>

<p>Why is this important? Well, these agents are incredibly powerful and help
me to <a href="/unenshittification/2026/02/22/Unenshittification-101.html">unenshittify my life</a>.
But they cannot be sandboxed and cannot be trusted as long as they have Bash access.
By running them in a Docker container, I can safely
run them in YOLO mode with no risk to my own Mac. They have all the tools they
need to create great apps, all without ever changing my development environment
or editing files outside of my repositories.</p>

<h2 id="libraries">Libraries</h2>

<p>A modern retro toolchain wouldn’t be complete without modern libraries to help
these retro systems do modern tasks. Altivec Intelligence includes critical
libraries bundled into dynamic frameworks and static libraries.</p>

<ul>
  <li>AltivecCocoa: Includes Font Awesome and other helper classes that make it easy to build applications that run across 20+ years of Apple history</li>
  <li>AltivecCore: Includes modern networking and database tooling to interact with modern web APIs
    <ul>
      <li>SQLite 3.43.2</li>
      <li>libcurl 7.88.1</li>
      <li>OpenSSL 1.1.1w</li>
      <li>zlib 1.2.13</li>
      <li>cJSON 1.7.19</li>
    </ul>
  </li>
</ul>

<h2 id="tooling">Tooling</h2>

<p>I will not go into all the details because you can view them on GitHub. But
Altivec Intelligence includes a lot of basic tooling to make it easy to 
build, deploy, and debug apps to <a href="/retro-tech/">retro devices</a>. This all relies on SSH key
authentication, which can also be customized because the container mounts the
home folder in ~/.altivec. This way you can always control which keys the 
container (and the agents) have access to. <code class="language-plaintext highlighter-rouge">altivec-deploy</code> is an included
script that can deploy your apps to Macs and to
jailbroken iPhones to start
<a href="/retro-tech/2025/12/09/PPC-iPhone-2.html">debugging sessions with GDB</a> or LLDB.
<code class="language-plaintext highlighter-rouge">altivec-release</code> automatically bumps
the versions of the apps in the Info.plist files.</p>

<h2 id="caveats">Caveats</h2>

<p>Altivec Intelligence is offered as a prebuilt Docker container for arm64 and x64
devices. It supports arm64 and x86-64 computers capable of running Docker.</p>

<p>It does not include Apple-proprietary tooling, so there is no support for NIBs,
for example. Deploying to an iPhone requires a jailbroken iPhone with SSH
installed. Supporting Mac OS X Tiger requires using a lowest common denominator
of Mac OS X APIs. The AI will help you do the runtime introspection, but it
can be a pain.</p>

<p>For usage instructions and example apps, see the project on GitHub.</p>

<p><a href="https://github.com/jeffreybergier/AltivecIntelligence">https://github.com/jeffreybergier/AltivecIntelligence</a></p>]]></content><author><name></name></author><category term="Unenshittification" /><category term="Apple" /><category term="Retro-Tech" /><category term="Objective-C" /><category term="Mac-OS-X" /><category term="iOS" /><category term="Docker" /><category term="AI" /><summary type="html"><![CDATA[Build small, personal apps for decades of Apple hardware without Xcode, Mac virtual machines, or permission from a platform owner]]></summary></entry><entry><title type="html">RSS-THE-PLANET</title><link href="https://jeffburg.com/unenshittification/2026/03/22/RSS-THE-PLANET.html" rel="alternate" type="text/html" title="RSS-THE-PLANET" /><published>2026-03-22T00:00:00+00:00</published><updated>2026-03-22T00:00:00+00:00</updated><id>https://jeffburg.com/unenshittification/2026/03/22/RSS-THE-PLANET</id><content type="html" xml:base="https://jeffburg.com/unenshittification/2026/03/22/RSS-THE-PLANET.html"><![CDATA[<h2 id="the-primary-feature-doomscrolling">The Primary Feature: Doomscrolling</h2>

<p>The dark secret of the vast majority of mobile apps we use, on
<a href="https://www.apple.com/os/ios/">iOS</a> and
<a href="https://www.android.com/">Android</a>, is that they are basically doomscrolling
apps. Now that does not mean that they are serving you malicious content
necessarily. But they all have the same basic UX and use the same basic
implementation details. Whether the app is:</p>

<ol>
  <li><a href="https://www.instagram.com/">Instagram</a></li>
  <li><a href="https://x.com/">Twitter / X</a></li>
  <li><a href="https://tinder.com/">Tinder</a></li>
  <li><a href="https://www.nytimes.com/">New York Times</a></li>
  <li><a href="https://www.tiktok.com/">TikTok</a></li>
  <li><a href="https://www.youtube.com/">YouTube</a></li>
  <li>Etc</li>
</ol>

<p>These apps all use the same basic technologies and features which are all really
simple in principle.</p>

<ol>
  <li>Fetch a list of content via <a href="https://www.rfc-editor.org/rfc/rfc8259">JSON</a></li>
  <li>Save or cache that data in an <a href="https://www.sqlite.org/">SQLite</a> database</li>
  <li>Convert that JSON into a model that is useful for the view (ViewModel)</li>
  <li>Display the list of ViewModels as a list in the UI
(<a href="https://developer.apple.com/documentation/uikit/uitableview">UITableView</a>)</li>
  <li>Asynchronously download related assets such as images and video</li>
</ol>

<p>It is important to point out that all of these capabilities have been built into
iOS since iOS 5. This is super simple and obvious behavior that all mobile apps
share.</p>

<h2 id="really-simple-syndication">Really Simple Syndication</h2>

<p>You may not know this, but there is a really old technology that enables all
of this functionality that is used by almost every website AND almost every
podcast on the internet called <a href="https://www.rssboard.org/rss-specification">RSS</a>.
RSS uses <a href="https://www.w3.org/XML/">XML</a> instead of JSON, but it enables the same
basic functionality using a category of apps called RSS readers. Common examples
are:</p>

<ol>
  <li><a href="https://netnewswire.com/">NetNewsWire</a></li>
  <li><a href="https://www.vienna-rss.com/">Vienna</a></li>
  <li><a href="https://reederapp.com/">Reeder</a></li>
</ol>

<h2 id="rss-the-planet">RSS-THE-PLANET</h2>

<p>RSS-THE-PLANET is a very simple
<a href="https://developers.cloudflare.com/workers/">Cloudflare Worker</a> application that
can encrypt and store your credentials for your doomscrolling apps and then
fetch their JSON APIs and convert them into normal RSS. Combine this with an RSS
reader and now you can enjoy your favorite doomscrolling content on a retro
device. For me, that retro device is an
<a href="https://support.apple.com/en-us/112016">iPhone 5</a> running Reeder on iOS 6.</p>

<p>In fact, this improves the experience because these RSS reader apps often
support offline viewing. So yeah, now I have easy access to all of my
doomscrolling content even when totally offline:</p>

<ol>
  <li><a href="https://joinmastodon.org/">Mastodon</a></li>
  <li><a href="https://www.apple.com/apple-news/">News</a></li>
  <li><a href="https://www.youtube.com/">YouTube</a></li>
  <li><a href="https://www.apple.com/apple-podcasts/">Podcasts</a></li>
</ol>

<p>RSS-THE-PLANET also includes basic features to proxy websites and images through
Cloudflare to downgrade their TLS restrictions to 1.0 or none at all
for older devices that do not support 
<a href="https://www.rfc-editor.org/rfc/rfc5246">TLS 1.2</a> natively.</p>

<h2 id="caveats">Caveats</h2>

<p>RSS-THE-PLANET is not a hosted service. But the code is MIT licensed and so you
can run it yourself for free in your Cloudflare account.</p>

<p><a href="https://github.com/jeffreybergier/RSS-THE-PLANET">https://github.com/jeffreybergier/RSS-THE-PLANET</a></p>

<p>Note that this approach is inherently insecure. While your credentials are
encrypted in Cloudflare, the generated RSS URLs allow unauthenticated access to 
your feeds. So please use with caution and keep your RSS feed links secret.</p>]]></content><author><name></name></author><category term="Unenshittification" /><category term="Apple" /><category term="Retro-Tech" /><category term="Cloudflare" /><category term="Serverless" /><category term="iOS" /><category term="Mac-OS-X" /><summary type="html"><![CDATA[Make your favorite doomscrolling apps work on any device by converting their simple JSON-based API into an old-fashioned RSS feed]]></summary></entry><entry><title type="html">2026: The Year of Unenshittification</title><link href="https://jeffburg.com/unenshittification/2026/02/22/Unenshittification-101.html" rel="alternate" type="text/html" title="2026: The Year of Unenshittification" /><published>2026-02-22T00:00:00+00:00</published><updated>2026-02-22T00:00:00+00:00</updated><id>https://jeffburg.com/unenshittification/2026/02/22/Unenshittification-101</id><content type="html" xml:base="https://jeffburg.com/unenshittification/2026/02/22/Unenshittification-101.html"><![CDATA[<h1 id="enshittification">Enshittification</h1>

<p>In 2026, I set a goal to <a href="https://en.wikipedia.org/wiki/Enshittification">unenshittify</a>
my digital life. I am old enough at this point that I came of age when the 
internet was new and exciting and was built with good intentions. But for the
past 10 years or so, it really has all changed for the worse. I won’t list all
of my grievances, but I think I can easily argue that the web is mostly dead,
apps have turned into cesspools of addictive nonsense, Mac apps are all now
Electron apps, and the reality is that what we have done with all of the 
amazing power of our modern computing devices is waste it all on heavyweight
frameworks, RAM-sucking JavaScript apps, and totally unwanted features. I am
done with this computing contract. This is not what I signed up for.</p>

<p>I am starting to sound like an old man, and that is OK because at least
I still have my Mac. Despite Apple’s best attempts, they have not been able to
suffocate the Mac like they have all of their other platforms. On my Mac, I am
in full control of my data, full control of the apps I use, and in full control
of the services my Mac runs. The iPhone, on the other hand, has turned into a
platform I absolutely despise. The App Store restrictions have turned one of
the most exciting and powerful computing platforms into an addictive 
doomscrolling appliance with little other purpose.</p>

<h1 id="complaints">Complaints</h1>

<p>The thing is that every app for the iPhone has never actually really changed.
Whether it’s 2012 or 2026, every app is fundamentally the same:</p>

<ol>
  <li>Download JSON</li>
  <li>Store JSON in a SQLite database</li>
  <li>Parse JSON into a list</li>
  <li>Fetch media while the list scrolls</li>
  <li>If I am being generous, on some chat apps there is some cryptography</li>
</ol>

<p>All of these capabilities are provided by Apple and built into the OS. So all
of these apps should be simple, small, and efficient. Yet they are not.
You can pick your favorite doomscrolling app from the App Store and they are 
all 300 MB+. Why is this? Well, it is different for every app. But usually it
comes down to:</p>

<ol>
  <li>Tracking frameworks</li>
  <li>Advertising frameworks</li>
  <li>Cross-platform UI frameworks</li>
  <li>Unwanted features</li>
  <li>etc., etc., etc.</li>
</ol>

<p>This combined with the fact that Apple keeps making the iPhone bigger, more
powerful, and more power-hungry, leaves me in such an awkward position. Since
the apps are nigh useless for anything except doomscrolling, please tell me
again why I need an 8” supercomputer with 12 GB of RAM in my pocket? All of this
stuff could be done with an iPhone 5 in 2012 if it wasn’t all enshittified.</p>

<h1 id="reaction">Reaction</h1>

<p>That’s what I am doing. I am switching to using an iPhone 5 as my main iPhone.
Why the iPhone 5? Well, it’s the newest iPhone that can run iOS 6 and it has
a widescreen and a Lightning connector, unlike the iPhone 4s. So I could do a 5s
or an SE and get the same size, but I thought it would be fun to have that 
retro design for the OS as well. Of course, I also have my lovely iMac G4
which would also benefit from the enhancements I made to the iPhone 5. This
would later turn out to be a bigger challenge than I thought, but I will save
that for later.</p>

<p>But long story short, my iPhone 5 is my day-to-day iPhone. I did also
downgrade my gigantic iPhone 14 Pro to an iPhone 13 mini which serves as my 
backup iPhone when I need to do things the iPhone 5 can’t… like make calls,
enable my Apple Watch (with Apple Pay), and use the enshittified apps that I 
have no choice but to use.</p>

<h1 id="ai-slop">AI Slop</h1>

<p>I will just say it up front. It is not possible for a single person working
in their spare time to recreate their digital life without AI. I will be
posting a lot about AI because it has honestly changed my life when it comes to
programming. But for the doubters out there, I have 2 primary arguments.</p>

<ol>
  <li>All of our favorite apps and services were all fully enshittified well
before AI was a thing. These apps became bloated 300 MB behemoths well before
AI. AI is not going to ruin software; it was already ruined. The software we 
have to use daily is 100% human-made slop.</li>
  <li>So much code we write basically does data conversions and data storage 
and retrieval; basically, taking JSON and storing it in a database or
reading from a database and converting data to JSON. This kind of code is 
not interesting, not fun to write, and we should not waste human time to
write this kind of boilerplate code (or crazy abstractions that attempt
to make this task tolerable).</li>
</ol>

<p>But AI will help me fix that, and for that reason, I love it.</p>

<h1 id="invitation">Invitation</h1>

<p>Follow along with me while I unenshittify my life. At the very least you can
have a good laugh at how much personal time I am <del>wasting</del> spending to 
recreate experiences that are already free. But don’t doubt too hard. For one,
they are my experiences created only for me. And two, I think you will be 
actually shocked at what is possible on these retro devices in 2026 when you have
the right build environments, toolchains, and a little creativity.</p>]]></content><author><name></name></author><category term="Unenshittification" /><category term="AI" /><category term="Apple" /><category term="Retro-Tech" /><category term="Objective-C" /><category term="iOS" /><category term="Mac-OS-X" /><summary type="html"><![CDATA[In 2026, I set a goal to unenshittify my digital life. This is the article where I justify my foolhardy actions]]></summary></entry><entry><title type="html">How to Sync iPhone 5 with PowerPC Macs</title><link href="https://jeffburg.com/retro-tech/2026/02/16/Reverse-iTunes-1.html" rel="alternate" type="text/html" title="How to Sync iPhone 5 with PowerPC Macs" /><published>2026-02-16T00:00:00+00:00</published><updated>2026-02-16T00:00:00+00:00</updated><id>https://jeffburg.com/retro-tech/2026/02/16/Reverse-iTunes-1</id><content type="html" xml:base="https://jeffburg.com/retro-tech/2026/02/16/Reverse-iTunes-1.html"><![CDATA[<table>
  <thead>
    <tr>
      <th style="width: 50%;">iMac G4 and iPhone 5</th>
      <th>iTunes Screenshots</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="vertical-align: top;">
        <a href="/assets/images/retro-tech/reverse-itunes-1/beauty-shot-1.jpeg">
          <img src="/assets/images/retro-tech/reverse-itunes-1/beauty-shot-1-thumb.jpeg" alt="iMac G4 Beauty Shot" style="width: 100%; height: auto;" />
        </a>
      </td>
      <td style="vertical-align: top;">
        <a href="/assets/images/retro-tech/reverse-itunes-1/sync-success-1.png">
          <img src="/assets/images/retro-tech/reverse-itunes-1/sync-success-1-thumb.png" alt="Sync Success 1" />
        </a>
        <a href="/assets/images/retro-tech/reverse-itunes-1/sync-success-2.png">
          <img src="/assets/images/retro-tech/reverse-itunes-1/sync-success-2-thumb.png" alt="Sync Success 2" />
        </a>
      </td>
    </tr>
  </tbody>
</table>

<h2 id="table-of-contents">Table of Contents</h2>

<ul id="markdown-toc">
  <li><a href="#table-of-contents" id="markdown-toc-table-of-contents">Table of Contents</a></li>
  <li><a href="#overview" id="markdown-toc-overview">Overview</a></li>
  <li><a href="#tldr" id="markdown-toc-tldr">TL;DR</a>    <ul>
      <li><a href="#tutorial" id="markdown-toc-tutorial">Tutorial</a></li>
      <li><a href="#caveats" id="markdown-toc-caveats">Caveats</a></li>
    </ul>
  </li>
  <li><a href="#background" id="markdown-toc-background">Background</a>    <ul>
      <li><a href="#but-why" id="markdown-toc-but-why">But Why</a></li>
    </ul>
  </li>
  <li><a href="#approach" id="markdown-toc-approach">Approach</a>    <ul>
      <li><a href="#0-run-itunes-with-gdb" id="markdown-toc-0-run-itunes-with-gdb">0. Run iTunes with GDB</a></li>
      <li><a href="#1-defeat-ptrace" id="markdown-toc-1-defeat-ptrace">1. Defeat PTRACE</a></li>
      <li><a href="#2-inspect-every-string-created" id="markdown-toc-2-inspect-every-string-created">2. Inspect Every String Created</a></li>
      <li><a href="#3-inspect-every-dictionary-created" id="markdown-toc-3-inspect-every-dictionary-created">3. Inspect Every Dictionary Created</a></li>
      <li><a href="#4-failed-use-gdb-to-replace-the-minitunesversion" id="markdown-toc-4-failed-use-gdb-to-replace-the-minitunesversion">4. [Failed] Use GDB to Replace the MinITunesVersion</a></li>
      <li><a href="#5-replace-the-key-directly-in-the-itunes-binary" id="markdown-toc-5-replace-the-key-directly-in-the-itunes-binary">5. Replace the Key Directly in the iTunes Binary</a></li>
      <li><a href="#6-future-work-iphone-artwork" id="markdown-toc-6-future-work-iphone-artwork">6. [Future Work] iPhone Artwork</a></li>
    </ul>
  </li>
</ul>

<h2 id="overview">Overview</h2>

<p>In this article, I will briefly show you how to get an iPhone 5 to sync
with any Mac running Mac OS X 10.5.8, and yes, this includes PowerPC Macs.
Syncing the iPhone 5 with PowerPC Macs was never possible because Apple
restricted the iPhone 5 to Mac OS X 10.6.8 or higher.</p>

<h2 id="tldr">TL;DR</h2>

<p>When the iPhone and iTunes communicate with each other, they do a handshake
involving a lot of property list (PLIST) XML passing. iTunes will start key
exchanges and then the iPhone will send a large PLIST payload that explains the
capabilities of the device such as the supported languages/regions, sync
features, and, most importantly, the minimum version of iTunes required by this
iPhone. This part of the PLIST is stored in the key “MinITunesVersion.” iTunes
then checks this key and if the value is larger than the current version, iTunes
will display an error saying which version of iTunes is required. Most
importantly, this key is not present when the iPhone 4s and iTunes do
their handshake.</p>

<p>So to bypass this check, all we need to do is change the “MinITunesVersion”
string hard-coded into the iTunes binary to anything else and iTunes will then
check for a key that does not exist in the PLIST XML provided by the iPhone
and then iTunes will happily not display an error and bring you straight to the
normal syncing screen in iTunes.</p>

<h3 id="tutorial">Tutorial</h3>

<ol>
  <li>Download your favorite binary hex editor for your PowerPC Mac, such as Hex Fiend
(<a href="https://www.macintoshrepository.org/25974-hex-fiend">Macintosh Repository</a>, <a href="https://macintoshgarden.org/apps/hex-fiend">Macintosh Garden</a>)</li>
  <li>In Finder, click Go→Go to Folder and type in <code class="language-plaintext highlighter-rouge">/Applications/iTunes.app/Contents/MacOS</code></li>
  <li>Back up the iTunes binary by right-clicking on it and choosing “Compress”</li>
  <li>Open the iTunes binary in the hex editor</li>
  <li>Search for the string “MinITunesVersion” (there should only be 1)</li>
  <li>Change it to literally anything else
    <ol>
      <li>I changed mine to “MinITuMesVersion”</li>
    </ol>
  </li>
  <li>Save the changes</li>
  <li>Relaunch iTunes; you’re good to go</li>
</ol>

<p>With this approach, I have been able to sync Apps, Music, Podcasts, iPhoto, and
Videos. Even the feature that allows you to rearrange your home screen from
iTunes works fine.</p>

<h3 id="caveats">Caveats</h3>

<ul>
  <li>This is a hack and may not work properly: hacker beware <i class="fa-solid fa-skull-crossbones"></i></li>
  <li>After performing this hack, iTunes forcefully exits when trying to play
any video. VLC has way better video playback performance so this is not a
problem for me.
    <ul>
      <li>I assume there is some sort of checksum iTunes checks before it tries to 
play video to protect DRM-protected content, but I have not tried to troubleshoot
this yet.</li>
    </ul>
  </li>
  <li>This might work for iPhone 5s and others but I have not tried</li>
  <li>This may not work on newer versions of iOS. I was using iOS 6.1.3 but if your
iPhone 5 is running a newer version, this may not work.
    <ul>
      <li>I confirmed this does not work if the iPhone is running iOS 10. I have not
yet tested on iOS 8.</li>
    </ul>
  </li>
</ul>

<h2 id="background">Background</h2>

<p>You may or may not remember, but when the iPhone 5 was released, its official
minimum system requirement was Mac OS X 10.6.8 Snow Leopard or newer. This of
course ruled out the ability to sync your iPhone 5 with a PowerPC Mac as those
only ran Mac OS X 10.5.8 Leopard at most. Given that Mac OS X 10.6 Snow Leopard
was released in August 2009 and the iPhone 5 was released in 2012, this seems
like a reasonable cutoff, especially in the fast-moving world of technology back
then.</p>

<p>If you Google this problem, you will find that there are many people online
(well, online back in 2012) complaining about this limitation and others
confidently making the <strong>wrong</strong> assertion that this is because of the switch
from the 30-pin dock connector to the Lightning connector. However, I was
extremely suspicious of this because when I open iTunes 10.6.3 and plug in the
iPhone 5, iTunes immediately shows a very nice error that says iTunes 10.7 is
required. Furthermore, the iPhone 5 shows up in Xcode! Xcode won’t deploy to the
device because iOS is too new, but it shows up and it can see it’s an iPhone 5
and which iOS version it’s running. So I knew that this Lightning issue was not
true; clearly, the iPhone was communicating with the Mac even over Lightning.</p>

<p>Given that my iPhone 5 and my iPhone 4s were both running iOS 6.1.3 and one of
them would happily sync with iTunes 10.6.3 while one would not, I knew this limitation
was artificial. And so began my first reverse-engineering project: Figure out
this artificial limitation and eliminate it.</p>

<h3 id="but-why">But Why</h3>

<p>Why did I spend so much time getting my iMac G4 to sync with an iPhone 5?
The answer is that I want to try to use an iOS 6 iPhone as my main iPhone in</p>
<ol>
  <li>What does that mean? Does that mean I won’t have a backup modern
iPhone? No, of course I will. It’s not possible to survive in the world without a
semi-modern mobile phone. But it will be my backup. I only have a few apps I use
on my iPhone and I think I can recreate them for iOS 6 without much issue. But
more on that later.</li>
</ol>

<p>But why not use my iPhone 4s; it’s running iOS 6 as well. Well, the iPhone 5 is
the newest iPhone that can run iOS 6, so the performance is much better, and it has 1 GB
of RAM <i class="fa-regular fa-face-surprise"></i> which seems insane to me. But
more importantly, the iPhone 5 has a widescreen display. Given so much
of what I do on the iPhone is YouTube, the lack of widescreen is kind of killer.
Also, the iPhone 5 can join 5 GHz Wi-Fi networks, and it has a Lightning connector.
Lightning cables are so important because they are plentiful, and I can
charge anywhere, even in 2026.</p>

<p>For these reasons I wanted to get my iPhone 5 working on my iMac G4 so I have a
full retro sync solution for all of my music, photos, podcasts, etc.</p>

<h2 id="approach">Approach</h2>

<p>At first, I thought the mechanism that prevented syncing with the iPhone 5 was
a whitelist in iTunes or in MobileDevice.framework. However, after many
hours searching both the disk and the iTunes and MobileDevice binaries in
Hopper, I was totally failing to find a whitelist of supported devices 
(Spoiler Alert: There isn’t one). So I had to change techniques. I had to
try to debug iTunes live in GDB.</p>

<p>I had never done disassembly or GDB on an app I didn’t make before. It’s
definitely an interesting process. I used a ton of Gemini to get this done 
(But I assume any AI will work). Gemini can’t think for you. But it can help
you with syntax and with knowing which functions in Core Foundation to set 
breakpoints on. But yeah, it can also totally lead you astray. There were
times when I was outright scolding it to try to get it back on the right path.</p>

<p>In the following few sections, I will walk you through how I defeated the iTunes
version restriction for the iPhone 5. Considering how simple the fix ended up
being, there are probably a lot of people out there wondering why no one did this
12 years ago when the iPhone 5 was new (me included).</p>

<h3 id="0-run-itunes-with-gdb">0. Run iTunes with GDB</h3>

<p>I used GDB to launch and debug iTunes. I used custom GDB files to set 
breakpoints and inspect the data flow between the iPhone and iTunes.</p>

<p>In every section below, I will include the GDB file so you get an idea of how
it works. To use the file, use the -x command in GDB.</p>

<p><code class="language-plaintext highlighter-rouge">gdb -x ~/Desktop/myfile.gdb /Applications/iTunes.app/Contents/MacOS/iTunes</code></p>

<h3 id="1-defeat-ptrace">1. Defeat PTRACE</h3>

<p>iTunes operates in a very defensive way. It’s not surprising given this is one
massively complex codebase and it deals with a lot of DRM content, purchasing,
etc. For this reason, iTunes does frequently use <code class="language-plaintext highlighter-rouge">ptrace</code> to check if it’s
connected to a debugger, and if so, it just quits. So the first thing I had to
figure out how to do was bypass this check.</p>

<p>It turns out it’s pretty easy. I set a breakpoint on the <code class="language-plaintext highlighter-rouge">ptrace</code> function and
then forcefully return 0. With that simple trick, it worked. With this in place,
iTunes happily runs while connected to the debugger <i class="fa-solid fa-user-ninja"></i></p>

<pre><code class="language-gdb"># Set a breakpoint on ptrace
break ptrace
commands
  # Optional, tells GDB to not print that the breakpoint was hit
  silent
  # Trick iTunes by forcing ptrace to return 0, no debugger attached
  return 0
  # Automatically continue so that the debugger doesn't pause
  continue
end

# Tells GDB to run the executable after it reads the file
run
</code></pre>

<h3 id="2-inspect-every-string-created">2. Inspect Every String Created</h3>

<p>I had very little to go on at this point. I had already searched the binaries in
Hopper thoroughly and I could not find any hard-coded references to any iPhone
identifiers. I had also exhaustively searched the whole filesystem for
references to iPhone identifiers. I was trying to find the whitelist for
supported devices somewhere, but I just couldn’t find it.</p>

<p>So I dropped down to the lowest common denominator in GDB. “What if I just print
every string that iTunes makes and see if I can find the whitelist in there?”
The short answer is I did not find the whitelist, but when observing the strings
flowing through the system, I noticed a lot of XML, especially after I plugged
in my phone. Perhaps this XML contained something interesting; perhaps indeed
<i class="fa-brands fa-hugging-face"></i></p>

<pre><code class="language-gdb">break ptrace
commands
  silent
  return 0
  continue
end

# Set a breakpoint on Core Foundation function that creates strings
fb CFStringCreateWithBytes
commands
  # Print register 4 which contains the raw data bytes
  x/s $r4
  continue
end

run
</code></pre>

<h3 id="3-inspect-every-dictionary-created">3. Inspect Every Dictionary Created</h3>

<p>Now that I was seeing XML get passed around, I thought this could be the key.
What is in all of this XML? I decided I would inspect it and see if there was
anything interesting. It turned out to be insanely interesting because once I 
plugged in any iPhone, iTunes and the iPhone would immediately start an
elaborate handshake via property lists. A ton of information was exchanged 
between them. This included the most critical piece!</p>

<p>It turns out that the iPhone was the one telling iTunes which version of iTunes
was required. <strong>THERE WAS NO WHITELIST!</strong> <i class="fa-solid fa-spoon"></i></p>

<blockquote>
  <p>Note that this file introduces a new GDB technique that Gemini showed me. When you
start printing all of these strings to the console, the app slows down
significantly. So at the beginning of the file, we set all the breakpoints we
need. Then at the end, we disable the one we don’t want (Breakpoint 2). Then
in a different breakpoint we re-enable that breakpoint.</p>

  <p>With this technique we don’t need to print every XML iTunes creates when it
boots up. Rather, we wait until the iPhone is plugged in <code class="language-plaintext highlighter-rouge">AMDeviceConnect</code> and
then enable the XML printing breakpoint.</p>
</blockquote>

<pre><code class="language-gdb">
break ptrace
commands
  silent
  return 0
  continue
end

# Set a breakpoint on Core Foundation function that 
# creates a dictionary from property list data
fb CFPropertyListCreateFromXMLData
commands
  # Get a pointer to the data (TODO: Is this needed???)
  set $bytes = (char*)CFDataGetBytePtr($r4)
  printf "%s\n", $bytes
  continue
end

# Set a breakpoint on the MobileDevice.framework function that 
# starts the iTunes &lt;-&gt; iPhone Handshake to enable the 
# CFPropertyListCreateFromXMLData breakpoint
fb AMDeviceConnect
commands
  # Re-enable breakpoint on CFPropertyListCreateFromXMLData
  enable 2
  continue
end

# Disable breakpoint on CFPropertyListCreateFromXMLData to improve performance. 
# Once AMDeviceConnect is hit, the breakpoint will be re-enabled
disable 2

run
</code></pre>

<p>The XML is huge, so I won’t put it all in here. But below is a sample, 
including the key we are interested in <code class="language-plaintext highlighter-rouge">MinITunesVersion</code>. Also, if you notice,
it says <code class="language-plaintext highlighter-rouge">MinMacOSVersion</code> is 10.5.8 which means if Apple had released iTunes
10.7 for 10.5.8, it would have worked without hacking. But I suppose what this
means is you might be able to sync with iTunes running on 10.4 if you also 
changed the <code class="language-plaintext highlighter-rouge">MinMacOSVersion</code> key in the iTunes binary. Of course, I have not
tried this so it might not work.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nt">&lt;key&gt;</span>GeniusConfigMaxVersion<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>20<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>GeniusConfigMinVersion<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>1<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>GeniusMetadataMaxVersion<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>20<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>GeniusMetadataMinVersion<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>1<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>GeniusSimilaritiesMaxVersion<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>20<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>GeniusSimilaritiesMinVersion<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>1<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>HomeScreenIconColumns<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>4<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>HomeScreenIconDockMaxCount<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>4<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>HomeScreenIconHeight<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>57<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>HomeScreenIconRows<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>5<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>HomeScreenIconWidth<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>57<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>HomeScreenNewsstand<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>IconFolderColumns<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>4<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>IconFolderMaxPages<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>1<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>IconFolderRows<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>4<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>IconStateSaves<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>KeyTypeSupportVersion<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>492<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>MinITunesVersion<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>10.7.0<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>MinMacOSVersion<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>10.5.8<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>NeedsAntiPhishingDB<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>OEMA<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>1<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>OEMID<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>0<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>PhotoEventsSupported<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>PhotoFacesSupported<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>PhotoVideosSupported<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>PlaylistFoldersSupported<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>PodcastsSupported<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>RentalsSupported<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>Ringtones<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
  <span class="nt">&lt;key&gt;</span>ScreenHeight<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>1136<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>ScreenScaleFactor<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>2<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>ScreenWidth<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;integer&gt;</span>640<span class="nt">&lt;/integer&gt;</span>
  <span class="nt">&lt;key&gt;</span>SupportedKeyboards<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;array&gt;</span>
    <span class="nt">&lt;string&gt;</span>ar<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;string&gt;</span>bg_BG<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;string&gt;</span>bo<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;string&gt;</span>ca_ES<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;string&gt;</span>chr<span class="nt">&lt;/string&gt;</span>
</code></pre></div></div>

<h3 id="4-failed-use-gdb-to-replace-the-minitunesversion">4. [Failed] Use GDB to Replace the MinITunesVersion</h3>

<p>Now that I knew exactly where the XML data was flowing through the system, I
thought I would try to change it in-flight with GDB and iTunes would be none the
wiser. However, this proved to be extremely difficult. Gemini walked me through
all sorts of possibilities. The one I thought would be the most straightforward
was setting a breakpoint on <code class="language-plaintext highlighter-rouge">CFDictionaryGetValue</code> and
<code class="language-plaintext highlighter-rouge">CFDictionaryGetValueIfPresent</code>. I could then check if the key was
<code class="language-plaintext highlighter-rouge">MinITunesVersion</code> and either return <code class="language-plaintext highlighter-rouge">10.6.3</code> or <code class="language-plaintext highlighter-rouge">null</code>. However, this turned
out to be very complex because CFString/NSString are a <a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html">class cluster</a> 
and have many underlying data types. From a developer’s perspective, there is no
difference, but when you are in GDB, you need to account for every possibility
in order to compare the string data. Also, if you use a high-level compare or
print function like <code class="language-plaintext highlighter-rouge">CFShow</code> or <code class="language-plaintext highlighter-rouge">CFStringCompare</code>, that allocates new memory
and that would cause iTunes to crash randomly as it’s not really safe to do that
in the debugger.</p>

<p>Note that even though the code snippet below is small, at this stage I spent
so many hours with Gemini trying to get a working solution that would patch
the <code class="language-plaintext highlighter-rouge">MinITunesVersion</code> in flight using GDB.</p>

<pre><code class="language-gdb">
break ptrace
commands
  silent
  return 0
  continue
end

# Set a breakpoint on Core Foundation function that 
# fetches the values from a dictionary using a key
fb CFDictionaryGetValue
commands
  # Even this simple command to print the key would eventually crash iTunes
  call (void)CFShow($r4)
  continue
end

fb AMDeviceConnect
commands
  enable 2
  continue
end

disable 2

run
</code></pre>

<h3 id="5-replace-the-key-directly-in-the-itunes-binary">5. Replace the Key Directly in the iTunes Binary</h3>

<p>It was only when this solution was not working that I started to use 
plain old-fashioned <code class="language-plaintext highlighter-rouge">grep</code> to search not only the entire filesystem on my
iMac G4 but also on my iPhone 5 for the key <code class="language-plaintext highlighter-rouge">MinITunesVersion</code>. I actually found
several interesting binaries on the iPhone 5 this way, but I have yet to look 
into them.</p>

<p>But on the iMac G4, it of course showed the plain-old iTunes binary contained
this key. That is when I thought, perhaps I can just change the key in the binary
and call it a day. Then iTunes would just use the wrong key when querying the
Property List provided by the iPhone. This could of course cause a crash if
the key was required and expected by iTunes. However, I found that when I 
watched the XML handshake between iTunes and my iPhone 4s,
<code class="language-plaintext highlighter-rouge">MinITunesVersion</code> was not present. This meant that iTunes was probably
considering this key optional, so it was safe to change it
<i class="fa-solid fa-microscope"></i></p>

<p>And that’s exactly what happened. I opened the iTunes binary in a hex editor,
searched for <code class="language-plaintext highlighter-rouge">MinITunesVersion</code>, changed it to <code class="language-plaintext highlighter-rouge">MinITuMesVersion</code>, and clicked “Save.”
After relaunching iTunes, I plugged in my iPhone 5 and it just showed up in 
iTunes with no fuss at all. Everything syncs just fine. I couldn’t believe it! 
<i class="fa-regular fa-face-surprise"></i></p>

<p><a href="/assets/images/retro-tech/reverse-itunes-1/hex-1.png"><img src="/assets/images/retro-tech/reverse-itunes-1/hex-1.png" alt="Hex Editor" /></a></p>

<h3 id="6-future-work-iphone-artwork">6. [Future Work] iPhone Artwork</h3>

<p>I want to get the correct artwork showing for the iPhone model in iTunes but
right now it shows a blank. I tried manually installing the artwork files from
iTunes 10.7 but that did not work (it also did not break anything). After doing
some digging in Hopper, it looks like iTunes has a huge function that was
probably code-generated where it selects the images. I tried for a bit to get
it working, but I had to stop as it’s a low priority.</p>

<p>But maybe one of these days…</p>

<p><a href="/assets/images/retro-tech/reverse-itunes-1/missing-artwork-1.png"><img src="/assets/images/retro-tech/reverse-itunes-1/missing-artwork-1.png" alt="Missing iPhone Artwork" /></a></p>]]></content><author><name></name></author><category term="Retro-Tech" /><category term="Reverse-Engineer" /><category term="PowerPC" /><category term="iOS" /><category term="Hobby" /><category term="iTunes" /><summary type="html"><![CDATA[Get an iPhone 5 to sync with a Mac running Mac OS X 10.5.8; this was never possible back in the day because Apple restricted the iPhone 5 to syncing only with Mac OS X 10.6.8 or higher.]]></summary></entry><entry><title type="html">How to Develop Original iPhone Apps in 2025</title><link href="https://jeffburg.com/retro-tech/2025/12/09/PPC-iPhone-2.html" rel="alternate" type="text/html" title="How to Develop Original iPhone Apps in 2025" /><published>2025-12-09T00:00:00+00:00</published><updated>2025-12-09T00:00:00+00:00</updated><id>https://jeffburg.com/retro-tech/2025/12/09/PPC-iPhone-2</id><content type="html" xml:base="https://jeffburg.com/retro-tech/2025/12/09/PPC-iPhone-2.html"><![CDATA[<table>
  <thead>
    <tr>
      <th style="width: 50%;">PowerPC Setup</th>
      <th>Xcode Screenshots</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="vertical-align: top;">
        <a href="/assets/images/retro-tech/ppc-iphone-1/run3.jpeg">
          <img src="/assets/images/retro-tech/ppc-iphone-1/run3-thumb.jpeg" alt="iMac G4 Beauty Shot" style="width: 100%; height: auto;" />
        </a>
      </td>
      <td style="vertical-align: top;">
        <a href="/assets/images/retro-tech/ppc-iphone-1/run2.png">
          <img src="/assets/images/retro-tech/ppc-iphone-1/run2-thumb.png" alt="Xcode Screenshot" />
        </a>
        <a href="/assets/images/retro-tech/ppc-iphone-1/run1.png">
          <img src="/assets/images/retro-tech/ppc-iphone-1/run1-thumb.png" alt="Xcode Screenshot" />
        </a>
      </td>
    </tr>
  </tbody>
</table>

<h2 id="table-of-contents">Table of Contents</h2>

<ul id="markdown-toc">
  <li><a href="#table-of-contents" id="markdown-toc-table-of-contents">Table of Contents</a></li>
  <li><a href="#overview" id="markdown-toc-overview">Overview</a>    <ul>
      <li><a href="#caveats" id="markdown-toc-caveats">Caveats</a></li>
      <li><a href="#approach" id="markdown-toc-approach">Approach</a></li>
      <li><a href="#system-requirements" id="markdown-toc-system-requirements">System Requirements</a></li>
    </ul>
  </li>
  <li><a href="#tutorial-prepare-the-computer-to-sign-iphone-apps-apple-developer-portal" id="markdown-toc-tutorial-prepare-the-computer-to-sign-iphone-apps-apple-developer-portal">Tutorial: Prepare the Computer to Sign iPhone Apps (Apple Developer Portal)</a>    <ul>
      <li><a href="#1-authority-install-the-latest-certificate-authorities-on-your-mac" id="markdown-toc-1-authority-install-the-latest-certificate-authorities-on-your-mac">1. Authority: Install the Latest Certificate Authorities on Your Mac</a></li>
      <li><a href="#2-certificate-create-iphone-development-signing-certificate" id="markdown-toc-2-certificate-create-iphone-development-signing-certificate">2. Certificate: Create iPhone Development Signing Certificate</a></li>
      <li><a href="#3-identity-create-wildcard-iphone-app-identity" id="markdown-toc-3-identity-create-wildcard-iphone-app-identity">3. Identity: Create Wildcard iPhone App Identity</a></li>
      <li><a href="#4-device-add-iphone-device-to-portal" id="markdown-toc-4-device-add-iphone-device-to-portal">4. Device: Add iPhone Device to Portal</a></li>
      <li><a href="#5-provision-create-provisioning-profile-combines-steps-2-3-and-4" id="markdown-toc-5-provision-create-provisioning-profile-combines-steps-2-3-and-4">5. Provision: Create Provisioning Profile (Combines Steps 2, 3, and 4)</a></li>
      <li><a href="#6-infoplist-edit-the-infoplist-and-build-settings-of-your-app" id="markdown-toc-6-infoplist-edit-the-infoplist-and-build-settings-of-your-app">6. Info.plist: Edit the Info.plist and Build Settings of Your App</a></li>
    </ul>
  </li>
  <li><a href="#tutorial-prepare-the-iphone-to-ignore-the-signature-jailbreak" id="markdown-toc-tutorial-prepare-the-iphone-to-ignore-the-signature-jailbreak">Tutorial: Prepare the iPhone to Ignore the Signature (Jailbreak)</a>    <ul>
      <li><a href="#1-jailbreak-restore-and-jailbreak-iphone-using-legacy-ios-kit" id="markdown-toc-1-jailbreak-restore-and-jailbreak-iphone-using-legacy-ios-kit">1. Jailbreak: Restore and Jailbreak iPhone using Legacy iOS Kit</a></li>
      <li><a href="#2-cydia-install-the-appsync-tweak-in-cydia" id="markdown-toc-2-cydia-install-the-appsync-tweak-in-cydia">2. Cydia: Install the AppSync Tweak in Cydia</a></li>
      <li><a href="#3-provision-install-the-provisioning-profile-on-device" id="markdown-toc-3-provision-install-the-provisioning-profile-on-device">3. Provision: Install the Provisioning Profile on Device</a></li>
      <li><a href="#4-run-run-app-on-device" id="markdown-toc-4-run-run-app-on-device">4. Run: Run App on Device</a></li>
    </ul>
  </li>
  <li><a href="#congratulations" id="markdown-toc-congratulations">Congratulations!</a></li>
</ul>

<h2 id="overview">Overview</h2>

<p>In this article we will cover how to deploy apps from Xcode to an original
iPhone running iPhone OS 2 or 3 in 2025. This will also work with iPhone 3G,
iPod Touch (1st Generation), and iPod Touch (2nd Generation). This is the second
article in the <a href="/retro-tech/2025/12/04/PPC-iPhone-1.html">"Enabling iPhone Development for PowerPC Macs"</a>
series but this no longer requires a PowerPC Mac. You can use any Mac running
10.7, 10.6, or 10.5 that has Xcode 3 or 4 with the iPhone SDK installed,
including a virtual machine (if you still have an Intel Mac).</p>

<p>There are a lot of fiddly steps, but I think it’s important to remember that this
is no different than what every iPhone developer had to do back in 2008. Now
Xcode has been enhanced so much that it’s all done for you. But back then, these
were totally normal steps.</p>

<h3 id="caveats">Caveats</h3>

<ul>
  <li>This requires an Apple Developer account (I am unsure if a free one will work 
or not)</li>
  <li>This requires a jailbroken iPhone or iPod Touch</li>
</ul>

<p>I think it is possible to do this without a developer account. I found a 
<a href="https://stackoverflow.com/a/10763454">Stack Overflow post</a> covering how to do
it. However, they all require the use of a jailbreak tool called 
<a href="https://cydia.saurik.com/codesign.html"><code class="language-plaintext highlighter-rouge">ldid</code></a>. I have not been able to find 
a copy of <code class="language-plaintext highlighter-rouge">ldid</code> compiled for PowerPC Macs. It might be possible to 
<a href="https://github.com/ProcursusTeam/ldid?tab=readme-ov-file">build from source</a>, 
but I have not tried.</p>

<h3 id="approach">Approach</h3>

<p>We are going to prepare Xcode to sign iPhone apps using the Apple Developer
Portal. Then we are going to prepare the iPhone to ignore the signature via a
jailbreak. This approach seems strange, but there are two sides of the equation.
Xcode will not complete the build process for iPhone apps that are intended for
the device without proper signatures. But then, these old iPhone OSes are so out
of date that they no longer contain valid certificate authorities to trust those
signatures, resulting in them refusing to launch the signed apps, thus requiring
the jailbreak.</p>

<p>In this tutorial we are going to be basically living in the “Organizer” in Xcode</p>
<ol>
  <li>Modern Xcode also has a window called Organizer, but it is significantly
different now. So you may want to check out 
<a href="https://archive.org/details/professional-xcode-3/page/616/mode/2up">"Professional Xcode: Chapter 22 Using the Organizer"</a>
on Archive.org to read more about Organizer. It actually has many capabilities
you would not expect such as capturing screenshots from the device. The book
also covers <a href="https://archive.org/details/professional-xcode-3/page/628/mode/2up">provisioning your iPhone</a>,
admittedly in less detail than this tutorial.</li>
</ol>

<h4 id="prepare-the-computer-to-sign-iphone-apps-apple-developer-portal">Prepare the Computer to Sign iPhone Apps (Apple Developer Portal)</h4>

<ol>
  <li><strong>Authority:</strong> Install the latest Certificate Authorities into your Mac</li>
  <li><strong>Certificate:</strong> Create iPhone Development Signing Certificate</li>
  <li><strong>Identity:</strong> Create Wildcard iPhone App Identity</li>
  <li><strong>Device:</strong> Add iPhone Device to Portal</li>
  <li><strong>Provision:</strong> Create Provisioning Profile (Combines Steps 2, 3, and 4)</li>
  <li><strong>Info Plist:</strong> Edit the Info.plist and Build Settings of Your App</li>
</ol>

<h4 id="prepare-the-iphone-to-ignore-the-signature-jailbreak">Prepare the iPhone to Ignore the Signature (Jailbreak)</h4>

<ol>
  <li><strong>Jailbreak:</strong> Restore and jailbreak iPhone using <a href="https://github.com/LukeZGD/Legacy-iOS-Kit#">Legacy iOS Kit</a></li>
  <li><strong>Cydia:</strong> Install the AppSync Tweak in Cydia</li>
  <li><strong>Provision:</strong> Install the Provisioning Profile on Device</li>
  <li><strong>Run:</strong> Run App on Device</li>
</ol>

<p>If anyone has advice on how to either not require the developer account to get
Xcode to deploy without a proper signing certificate OR anyone knows how to get
the iPhone to launch the properly signed app without a jailbreak, please let me
know, so I can enhance the tutorial. I think it is frustrating to BOTH need a 
developer account AND need a jailbreak. It should be one or the other, not both.</p>

<h3 id="system-requirements">System Requirements</h3>

<ul>
  <li>A Mac or Virtual Machine that is running Mac OS X 10.7, 10.6, or 10.5</li>
  <li>A Mac with USB 2.0 (it might work with USB 1.1 but no promises)</li>
  <li>Xcode 4.x.x, 3.2.x, or 3.1.4 (The screenshots in this article are for Xcode 3)</li>
  <li>Original iPhone, iPhone 3G, iPod Touch (1st Generation), iPod Touch 
(2nd Generation)</li>
  <li>Device running iPhone OS software 3.1.3 or lower
    <ul>
      <li>These devices can easily be downgraded using <a href="https://github.com/LukeZGD/Legacy-iOS-Kit">Legacy iOS Kit</a></li>
      <li>Legacy iOS Kit requires an Intel or Apple Silicon Mac or a Linux computer</li>
    </ul>
  </li>
</ul>

<h2 id="tutorial-prepare-the-computer-to-sign-iphone-apps-apple-developer-portal">Tutorial: Prepare the Computer to Sign iPhone Apps (Apple Developer Portal)</h2>

<h3 id="1-authority-install-the-latest-certificate-authorities-on-your-mac">1. Authority: Install the Latest Certificate Authorities on Your Mac</h3>

<p>Before any of this will work, we need to make sure your ancient version of
Mac OS X is loaded with the latest Apple Certificate Authorities and 
Intermediate Certificates that Apple uses for signing its development
certificates.</p>

<table>
  <thead>
    <tr>
      <th>Apple PKI</th>
      <th>Open Panel</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/auth1.png"><img src="/assets/images/retro-tech/ppc-iphone-2/auth1-thumb.png" alt="Apple PKI Page" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/auth2.png"><img src="/assets/images/retro-tech/ppc-iphone-2/auth2-thumb.png" alt="Apple PKI Page" /></a></td>
    </tr>
  </tbody>
</table>

<ol>
  <li>Open <a href="https://www.apple.com/certificateauthority/">Apple’s PKI page</a></li>
  <li>Download the following certificate files. Depending on your browser you may 
need to hold the Option key while clicking on them to download the certificate
files.
    <ol>
      <li>“Apple Inc. Root”</li>
      <li>“Apple Root CA - G2 Root”</li>
      <li>“Apple Root CA - G3 Root”</li>
    </ol>
  </li>
  <li>Double-click them one by one. In the Keychain Access open panel, it will
give you a choice of which keychain to add them to. Choose the System 
keychain, not the Login keychain.</li>
  <li>Now do the same for the Worldwide Developer Relations intermediate certificates.
    <ol>
      <li>Worldwide Developer Relations - G2</li>
      <li>Worldwide Developer Relations - G3</li>
      <li>Worldwide Developer Relations - G4</li>
      <li>Worldwide Developer Relations - G5</li>
      <li>Worldwide Developer Relations - G6</li>
    </ol>
  </li>
</ol>

<p>Now your Mac should trust the certificates issued by Apple.</p>

<h3 id="2-certificate-create-iphone-development-signing-certificate">2. Certificate: Create iPhone Development Signing Certificate</h3>

<p>This is the most tedious part. You should probably make a folder on your system
for storing all the output of this step, so you can zip it all up at the end
as a backup. This should all be possible on your vintage Mac via AquaFox
(PowerPC) or ArcticFox (Intel), but I chose to use my modern Mac to do all of
the Apple Portal stuff. This means I needed to transfer files back and
forth between the old system and the new system.</p>

<h4 id="a-make-your-certificate-signing-request">A. Make Your Certificate Signing Request</h4>

<table>
  <thead>
    <tr>
      <th>Select Apple CA</th>
      <th>Certificate Signing Request</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cert1.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cert1-thumb.png" alt="Keychain" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cert2.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cert2-thumb.png" alt="CSR" /></a></td>
    </tr>
  </tbody>
</table>

<p>Follow the <a href="https://developer.apple.com/help/account/certificates/create-a-certificate-signing-request/">instructions on this page</a> 
to make a Certificate Signing Request. Note that these instructions did not work
for me at first. The Assistant said it had made my request and saved it to the
Desktop, but it had not. To resolve this, I went into the System keychain and
selected the Apple Root CA before starting the Assistant. I am not sure if
it matters which certificate you select, but it seems like you need to select
something first.</p>

<p>The way I could tell that it was going to work was that the assistant displayed the
checkbox in the UI that says “Let me specify key pair information.” When that
checkbox was present, then the assistant would actually make the Certificate
Signing Request file.</p>

<h4 id="b-make-a-signing-certificate">B. Make a Signing Certificate</h4>

<table>
  <thead>
    <tr>
      <th>Prompt 1</th>
      <th>Prompt 2</th>
      <th>Certificate Download</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cert3.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cert3-thumb.png" alt="Prompt 1" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cert4.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cert4-thumb.png" alt="Prompt 2" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cert5.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cert5-thumb.png" alt="Prompt 2" /></a></td>
    </tr>
  </tbody>
</table>

<ol>
  <li>Open <a href="https://developer.apple.com/account/resources/certificates/list">Apple Developer Portal - Certificates</a> and click the + button next to Certificates</li>
  <li>Follow the prompts, all the default options are fine</li>
  <li>Download the certificate when you get to the end</li>
</ol>

<h4 id="c-add-the-signing-certificate-to-the-keychain">C. Add the Signing Certificate to the Keychain</h4>

<table>
  <thead>
    <tr>
      <th>All Items</th>
      <th>My Certificates</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cert6.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cert6-thumb.png" alt="All Items" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cert7.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cert7-thumb.png" alt="My Certificates" /></a></td>
    </tr>
  </tbody>
</table>

<p>Double-click to add the Signing Certificate to the Login keychain. Note
that the Signing Certificate MUST appear in the “My Certificates” section of
Keychain Access. If you see it in the “All Items” section but not in the 
“My Certificates” section, then Xcode will not properly see it.</p>

<p>This happened to me and I used Gemini to help me fix it. You can see
<a href="/assets/images/retro-tech/ppc-iphone-2/cert8.png">the full conversation here</a>, 
but the summary is that you need to export the private key as a P12 file, 
then delete all three certificates from Keychain, then re-add the P12 file and then
re-add the Signing Certificate. That did it for me. I have no idea why.</p>

<h3 id="3-identity-create-wildcard-iphone-app-identity">3. Identity: Create Wildcard iPhone App Identity</h3>

<table>
  <thead>
    <tr>
      <th>Prompt 1</th>
      <th>Prompt 2</th>
      <th>Prompt 3</th>
      <th>Prompt 4</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/ident1.png"><img src="/assets/images/retro-tech/ppc-iphone-2/ident1-thumb.png" alt="Prompt 1" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/ident2.png"><img src="/assets/images/retro-tech/ppc-iphone-2/ident2-thumb.png" alt="Prompt 1" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/ident3.png"><img src="/assets/images/retro-tech/ppc-iphone-2/ident3-thumb.png" alt="Prompt 1" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/ident4.png"><img src="/assets/images/retro-tech/ppc-iphone-2/ident4-thumb.png" alt="Prompt 1" /></a></td>
    </tr>
  </tbody>
</table>

<p>Now you are going to create a “wildcard” App Identity. Wildcard means we just 
need to specify the base part of the bundle identifier and the part where the
app name goes is just an *. If you are an iOS developer, you may already have
an established base name. But if not, you can just use the Bonjour name of the
Mac you are running Xcode on. I will do that and so it will be
<code class="language-plaintext highlighter-rouge">local.IchigoDaifuku.*</code>. You will need to type this name into the Info.plist
of every app you make, so make it something easy to remember.</p>

<ol>
  <li>Open <a href="https://developer.apple.com/account/resources/identifiers/list">Apple Developer Portal - Identifiers</a> and click the + button next to Identifiers</li>
  <li>Follow the prompts, all the default options are fine</li>
  <li>When it asks you for the bundle identifier, click the “Wildcard” radio button
and type in your identifier</li>
  <li>Click “Register”</li>
  <li>There is nothing to download in this step, so you can move to the next step</li>
</ol>

<h3 id="4-device-add-iphone-device-to-portal">4. Device: Add iPhone Device to Portal</h3>

<table>
  <thead>
    <tr>
      <th>Device Error</th>
      <th>Copy Device ID</th>
      <th>Developer Portal</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/device1.png"><img src="/assets/images/retro-tech/ppc-iphone-2/device1-thumb.png" alt="Device Error" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/device2.png"><img src="/assets/images/retro-tech/ppc-iphone-2/device2-thumb.png" alt="Copy Device ID" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/device3.png"><img src="/assets/images/retro-tech/ppc-iphone-2/device3-thumb.png" alt="Developer Portal" /></a></td>
    </tr>
  </tbody>
</table>

<p>Now you are going to add your device to the Apple Developer Portal so that we
can generate a provisioning profile in the next step.</p>

<ol>
  <li>In Xcode, choose Window→Organizer and then plug in your device</li>
  <li>If you see the Device Error above, then this version of Xcode is not 
compatible with your device or the OS that’s installed on it.</li>
  <li>Right-click the device name in the sidebar and choose “Copy Device
Identifier”</li>
  <li>Open <a href="https://developer.apple.com/account/resources/devices/list">Apple Developer Portal - Devices</a> and click the + button next to Devices</li>
  <li>Go through the prompts and paste in the Device ID when requested</li>
  <li>There is nothing to download after this step, so you can move on to 
provisioning</li>
</ol>

<p>If you got the error above that your device is not compatible, please verify
your device version, the software version, and the Xcode version. These all
need to be compatible to move on. If it’s an iPhone 4 or lower, then the device
software can easily be downgraded via <a href="https://github.com/LukeZGD/Legacy-iOS-Kit#">Legacy iOS Kit</a>.</p>

<h3 id="5-provision-create-provisioning-profile-combines-steps-2-3-and-4">5. Provision: Create Provisioning Profile (Combines Steps 2, 3, and 4)</h3>

<table>
  <thead>
    <tr>
      <th>Prompt 1</th>
      <th>Prompt 2</th>
      <th>Prompt 3</th>
      <th>Prompt 4</th>
      <th>Prompt 5</th>
      <th>Prompt 6</th>
      <th>Organizer</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/profile1.png"><img src="/assets/images/retro-tech/ppc-iphone-2/profile1-thumb.png" alt="Prompt 1" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/profile2.png"><img src="/assets/images/retro-tech/ppc-iphone-2/profile2-thumb.png" alt="Prompt 2" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/profile3.png"><img src="/assets/images/retro-tech/ppc-iphone-2/profile3-thumb.png" alt="Prompt 3" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/profile4.png"><img src="/assets/images/retro-tech/ppc-iphone-2/profile4-thumb.png" alt="Prompt 4" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/profile5.png"><img src="/assets/images/retro-tech/ppc-iphone-2/profile5-thumb.png" alt="Prompt 5" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/profile6.png"><img src="/assets/images/retro-tech/ppc-iphone-2/profile6-thumb.png" alt="Prompt 6" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/profile7.png"><img src="/assets/images/retro-tech/ppc-iphone-2/profile7-thumb.png" alt="Organizer" /></a></td>
    </tr>
  </tbody>
</table>

<p>Now you are going to create the provisioning profile. This step combines the
signing certificate (Step 2), the app identifier (Step 3), and the device
identifier (Step 4) into the final step. Please be very careful when going
through the prompts on the Apple Developer Portal as any mistake will make the 
provisioning profile invalid.</p>

<ol>
  <li><strong>Open</strong> <a href="https://developer.apple.com/account/resources/profiles/list">Apple Developer Portal - Profiles</a> and click the + button next to Profiles</li>
  <li><strong>Prompt 1:</strong> Choose “iOS App Development”</li>
  <li><strong>Prompt 2:</strong> Choose the App Identifier you made in Step 3</li>
  <li><strong>Prompt 3:</strong> Choose the Certificate you made in Step 2. The names can be 
pretty vague but I think the bottom one is the newest</li>
  <li><strong>Prompt 4:</strong> Choose the Device you added in Step 4</li>
  <li><strong>Prompt 5:</strong> Give the Profile a name</li>
  <li><strong>Prompt 6:</strong> Download the provisioning profile</li>
  <li><strong>Organizer:</strong> In Xcode, choose Window→Organizer. In the sidebar, select 
Provisioning Profiles and then drag the profile from the Finder into the 
Organizer</li>
  <li>Now you can ZIP up all the uploads and downloads created during this part
of the tutorial for safekeeping, in case they are ever needed again</li>
</ol>

<h3 id="6-infoplist-edit-the-infoplist-and-build-settings-of-your-app">6. Info.plist: Edit the Info.plist and Build Settings of Your App</h3>

<table>
  <thead>
    <tr>
      <th>Info.plist</th>
      <th>Build Settings 1</th>
      <th>Build Settings 2</th>
      <th>Build and Go</th>
      <th>No Device Error</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/info1.png"><img src="/assets/images/retro-tech/ppc-iphone-2/info1-thumb.png" alt="Info.plist" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/info2.png"><img src="/assets/images/retro-tech/ppc-iphone-2/info2-thumb.png" alt="Build Settings 1" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/info3.png"><img src="/assets/images/retro-tech/ppc-iphone-2/info3-thumb.png" alt="Build Settings 2" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/info4.png"><img src="/assets/images/retro-tech/ppc-iphone-2/info4-thumb.png" alt="Build and Go" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/info5.png"><img src="/assets/images/retro-tech/ppc-iphone-2/info5-thumb.png" alt="No Device Error" /></a></td>
    </tr>
  </tbody>
</table>

<p>Now you are going to edit the Info.plist and the build settings of your 
application. If you don’t have an application, you can just create one from the
templates under File→New Project. And yes, you will need to do this any time
you create a new application project.</p>

<ol>
  <li>In Xcode, open Info.plist and change the bundle identifier to match Step 3. 
Type Command + S to save the file</li>
  <li>Open the Build Settings by selecting the project file at the top of the 
Groups &amp; Files pane and then click on Info in the toolbar</li>
  <li>In the info panel, click on the Build tab and then make sure the Configuration
and Show dropdowns say “All”</li>
  <li>In the Build Settings panel, look for Code Signing Identity, and choose the 
provisioning profile you created in Step 5 and then close the info panel</li>
  <li>Make sure there is no device connected, then choose Device from the Overview 
dropdown and then click Build and Go</li>
  <li>At this point, you might see a Keychain access permission prompt; choose “Always
Allow”</li>
  <li>Lastly you should see an error appear that says “No provisioned iPhone OS 
device is connected”</li>
</ol>

<p>If you receive the “No provisioned iPhone OS device is connected” error, you
have succeeded, congratulations! You have just gone through the really tedious
task that Apple created for all iPhone developers back in 2008. Now Xcode is 100%
ready to deploy your custom apps to a real iPhone!</p>

<p>If you see a different error, like a build or signing error, then you have a 
different problem that you need to troubleshoot before you can move on.</p>

<h2 id="tutorial-prepare-the-iphone-to-ignore-the-signature-jailbreak">Tutorial: Prepare the iPhone to Ignore the Signature (Jailbreak)</h2>

<p>This is the part of the tutorial that an iPhone developer would not have had to
do in 2008. I am not quite sure the reason, but iPhone OS 3.1.3 no longer trusts
the signature we just worked so hard to enable in Xcode. Yes, we had to do it
because Xcode will not deploy apps to a device without a valid signature. But if
you try to plug in your device and deploy to it right now, you will get an error
telling you that the app could not be installed because there were certificate
issues and it suggests checking the date and time on the phone. I think what
has happened is the Certificate Authorities that Apple used in 2008 have expired
since then and are no longer valid. I tried installing the CAs from Step 1
of this tutorial on the phone, but I still got the same error.</p>

<p>It’s OK though, because honestly, these old devices are pretty much bricks
without a jailbreak. Also, jailbreaking is so incredibly easy now thanks
to <a href="https://github.com/LukeZGD/Legacy-iOS-Kit#">Legacy iOS Kit</a> by LukeZGD. 
If you use this tool successfully, you should definitely 
<a href="https://ko-fi.com/lukezgd">buy him a coffee</a> because the script is amazing!
It even helps you enter DFU mode with exactly timed instructions.</p>

<h3 id="1-jailbreak-restore-and-jailbreak-iphone-using-legacy-ios-kit">1. Jailbreak: Restore and Jailbreak iPhone using Legacy iOS Kit</h3>

<table>
  <thead>
    <tr>
      <th>Main Menu</th>
      <th>Select OS</th>
      <th>Download IPSW</th>
      <th>Start Restore</th>
      <th>Restore</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/jail1.png"><img src="/assets/images/retro-tech/ppc-iphone-2/jail1-thumb.png" alt="Main Menu" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/jail2.png"><img src="/assets/images/retro-tech/ppc-iphone-2/jail2-thumb.png" alt="Select OS" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/jail3.png"><img src="/assets/images/retro-tech/ppc-iphone-2/jail3-thumb.png" alt="Download IPSW" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/jail4.png"><img src="/assets/images/retro-tech/ppc-iphone-2/jail4-thumb.png" alt="Start Restore" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/jail5.png"><img src="/assets/images/retro-tech/ppc-iphone-2/jail5-thumb.png" alt="Restore" /></a></td>
    </tr>
  </tbody>
</table>

<p>I will provide the instructions for performing a restore and jailbreak, which
will erase everything on the device. However, if your device is a time capsule,
and you do not want to erase it, I totally understand. 
<a href="https://github.com/LukeZGD/Legacy-iOS-Kit#">Legacy iOS Kit</a> can also just 
jailbreak it for you. Please see the <a href="https://github.com/LukeZGD/Legacy-iOS-Kit/wiki/Jailbreaking-with-Legacy-iOS-Kit">documentation</a>.</p>

<ol>
  <li>On a modern-ish Intel or Apple Silicon Mac, open Terminal: <code class="language-plaintext highlighter-rouge">cd Downloads</code></li>
  <li>Clone Legacy iOS Kit: <code class="language-plaintext highlighter-rouge">git clone https://github.com/LukeZGD/Legacy-iOS-Kit.git</code></li>
  <li><code class="language-plaintext highlighter-rouge">cd</code> into the directory: <code class="language-plaintext highlighter-rouge">cd Legacy-iOS-Kit</code></li>
  <li>Run the setup script: <code class="language-plaintext highlighter-rouge">./restore.sh</code></li>
  <li>Plug in your device and then run the script again: <code class="language-plaintext highlighter-rouge">./restore.sh</code></li>
  <li>Follow the onscreen prompts to restore
    <ol>
      <li>Please read the prompts carefully and see the screenshots above for a full
visual walkthrough</li>
      <li>Note that the script will offer to download the latest version of the OS
for you, but if you want to select your own version you can download it 
from <a href="https://ipsw.me">IPSW.me</a></li>
    </ol>
  </li>
</ol>

<p>The device should now be restored and jailbroken. If you can’t unlock it
(meaning it is stuck at the slider that says Slide for Emergency), then run
the script again and it will have an option to attempt activation. If there is
a SIM card in the device, this should work without the Hacktivate option. But
sometimes you have to try the “lighter” activation thing two or three times.</p>

<h3 id="2-cydia-install-the-appsync-tweak-in-cydia">2. Cydia: Install the AppSync Tweak in Cydia</h3>

<table>
  <thead>
    <tr>
      <th>Add Source</th>
      <th>Install 1</th>
      <th>Install 2</th>
      <th>Install 3</th>
      <th>Install 4</th>
      <th>Install 5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cydia1.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cydia1-thumb.png" alt="Add Source" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cydia2.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cydia2-thumb.png" alt="Install 1" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cydia3.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cydia3-thumb.png" alt="Install 2" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cydia4.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cydia4-thumb.png" alt="Install 3" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cydia5.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cydia5-thumb.png" alt="Install 4" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/cydia6.png"><img src="/assets/images/retro-tech/ppc-iphone-2/cydia6-thumb.png" alt="Install 5" /></a></td>
    </tr>
  </tbody>
</table>

<p>Now you will be installing a special tweak for the iPhone called AppSync. This
tweak will prevent the process called <code class="language-plaintext highlighter-rouge">installd</code> from checking for signatures
when apps are installed. This is critical to installing apps from iTunes,
Xcode, or even Legacy iOS Kit as this one process checks all of them for a 
valid signature before installing.</p>

<p>There is a different version of AppSync for different iOS versions, so see the
<a href="https://github.com/LukeZGD/Legacy-iOS-Kit/wiki/Install-IPA-AppSync">Legacy iOS Kit Documentation</a> 
for full details. In this tutorial, I will be showing this for iPhone OS 3.1.3.</p>

<ol>
  <li><strong>Open Cydia:</strong> On first launch, Cydia may need to do a one-time setup and then
restart</li>
  <li><strong>Show Sources:</strong> Tap on the Sources tab and wait for it to refresh (it could
take a while)</li>
  <li><strong>(Optional) Delete Bad Sources:</strong> For sources that failed to refresh, delete
them. They will have no icon and if you tap on them, they will have no 
packages</li>
  <li><strong>Add Source:</strong> Tap the “Edit” button at the top right and then tap “Add” at 
the top left and type <code class="language-plaintext highlighter-rouge">http://ios3.party</code></li>
  <li><strong>Install AppSync:</strong> Tap “iPhone OS 3 Party,” tap “All Packages,” tap
“AppSync for OS 3.1,” tap “Modify,” then tap “Install,” and lastly tap
“Confirm”</li>
  <li><strong>Restart:</strong> After you see the button that says “Return to Cydia”, you can 
tap it and then restart the device</li>
</ol>

<h3 id="3-provision-install-the-provisioning-profile-on-device">3. Provision: Install the Provisioning Profile on Device</h3>

<table>
  <thead>
    <tr>
      <th>Add Profile</th>
      <th>Profile Installed</th>
      <th>Device Verify</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/provision1.png"><img src="/assets/images/retro-tech/ppc-iphone-2/provision1-thumb.png" alt="Add Profile" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/provision2.png"><img src="/assets/images/retro-tech/ppc-iphone-2/provision2-thumb.png" alt="Profile Installed" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/provision3.png"><img src="/assets/images/retro-tech/ppc-iphone-2/provision3-thumb.png" alt="Device Verification" /></a></td>
    </tr>
  </tbody>
</table>

<p>The last step before running an app is to connect it to your vintage Mac and
ask the Mac to use it for development.</p>

<ol>
  <li><strong>Organizer:</strong> In Xcode, choose Window→Organizer and then plug in your device</li>
  <li><strong>Use for Development:</strong> Select your device in the sidebar and then click 
“Use for Development” (sorry, no screenshot because this is a one-time action)</li>
  <li><strong>Add Profile:</strong> Under the Provisioning section, click the + button to add 
your provisioning profile</li>
  <li><strong>Device Verify:</strong> On your device, open Settings, then tap General, then tap 
Profiles to verify your provisioning profile was installed</li>
</ol>

<h3 id="4-run-run-app-on-device">4. Run: Run App on Device</h3>

<table>
  <thead>
    <tr>
      <th>Xcode Breakpoint</th>
      <th>iPhone Paused</th>
      <th>Xcode Paused</th>
      <th>iPhone Running</th>
      <th>Xcode Error</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/run1.png"><img src="/assets/images/retro-tech/ppc-iphone-2/run1-thumb.png" alt="Xcode Set Breakpoint" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/run2.png"><img src="/assets/images/retro-tech/ppc-iphone-2/run2-thumb.png" alt="iPhone Paused" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/run3.png"><img src="/assets/images/retro-tech/ppc-iphone-2/run3-thumb.png" alt="Xcode Pause Breakpoint" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/run4.png"><img src="/assets/images/retro-tech/ppc-iphone-2/run4-thumb.png" alt="iPhone Running" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-2/run5.png"><img src="/assets/images/retro-tech/ppc-iphone-2/run5-thumb.png" alt="Xcode Error" /></a></td>
    </tr>
  </tbody>
</table>

<p>You’re probably pretty tired by now, but hang in there, we are about to finally 
achieve our goal! Running your custom app on your vintage iPhone with the full
debugger and all the goodies.</p>

<ol>
  <li>Open your app project in Xcode, then follow numbers 1 through 5 in the Xcode 
Breakpoint screenshot above:
    <ol>
      <li>① Open <code class="language-plaintext highlighter-rouge">main.m</code></li>
      <li>② Set a breakpoint on or before UIApplicationMain (click in that little 
gutter area)</li>
      <li>③ Make sure the Overview menu is set to run on Device</li>
      <li>④ Click to change to the debugging view</li>
      <li>⑤ Click Build and Go</li>
    </ol>
  </li>
  <li>Wait 30–60 seconds and you should see your app launch on your phone and it
will pause at the launch image</li>
  <li>In Xcode, you will see the breakpoint was hit; click the Continue button</li>
  <li>On your phone, your app will now be running</li>
  <li>If you see the error in the screenshot above with the selected console output,
that means that AppSync from the previous step is not installed or not 
working properly and the iPhone is still checking signatures</li>
</ol>

<h2 id="congratulations">Congratulations!</h2>

<p><i class="apl-computer-imac-g4-17-256 reflect below-sm round-none"></i>
<i class="apl-device-iphone-128 reflect below-lg round-none"></i>
<i class="apl-computer-imac-aluminum-20-256 reflect below-sm round-none"></i></p>

<p>You now have a setup that can build and deploy apps to your vintage iPhone!
Please enjoy the process of making vintage apps and working with old-school
Objective-C. It’s kind of a pain in the butt, but I think you will enjoy its
simplicity and flexibility.</p>

<p>If you want to run MameDaifuku, my sample app that lists out some build and
runtime information, you can clone or download it from GitHub.
<a href="https://github.com/jeffreybergier/MameDaifuku">https://github.com/jeffreybergier/MameDaifuku</a></p>]]></content><author><name></name></author><category term="Retro-Tech" /><category term="Apps" /><category term="PowerPC" /><category term="iOS" /><category term="Hobby" /><summary type="html"><![CDATA[iOS developers out there, have you ever been interested in stepping back from the new hotness for a moment to revisit your roots and try developing apps for the original iPhone? This article will walk you through the whole process. A lot of things have changed since 2008, but it's still possible in 2025.]]></summary></entry><entry><title type="html">Enabling iPhone Development for PowerPC Macs</title><link href="https://jeffburg.com/retro-tech/2025/12/04/PPC-iPhone-1.html" rel="alternate" type="text/html" title="Enabling iPhone Development for PowerPC Macs" /><published>2025-12-04T00:00:00+00:00</published><updated>2025-12-04T00:00:00+00:00</updated><id>https://jeffburg.com/retro-tech/2025/12/04/PPC-iPhone-1</id><content type="html" xml:base="https://jeffburg.com/retro-tech/2025/12/04/PPC-iPhone-1.html"><![CDATA[<table>
  <thead>
    <tr>
      <th style="width: 50%;">PowerPC Setup</th>
      <th>Xcode Screenshots</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="vertical-align: top;">
        <a href="/assets/images/retro-tech/ppc-iphone-1/run3.jpeg">
          <img src="/assets/images/retro-tech/ppc-iphone-1/run3-thumb.jpeg" alt="iMac G4 Beauty Shot" style="width: 100%; height: auto;" />
        </a>
      </td>
      <td style="vertical-align: top;">
        <a href="/assets/images/retro-tech/ppc-iphone-1/run2.png">
          <img src="/assets/images/retro-tech/ppc-iphone-1/run2-thumb.png" alt="Xcode Screenshot" />
        </a>
        <a href="/assets/images/retro-tech/ppc-iphone-1/run1.png">
          <img src="/assets/images/retro-tech/ppc-iphone-1/run1-thumb.png" alt="Xcode Screenshot" />
        </a>
      </td>
    </tr>
  </tbody>
</table>

<h2 id="table-of-contents">Table of Contents</h2>

<ul id="markdown-toc">
  <li><a href="#table-of-contents" id="markdown-toc-table-of-contents">Table of Contents</a></li>
  <li><a href="#overview" id="markdown-toc-overview">Overview</a>    <ul>
      <li><a href="#caveats" id="markdown-toc-caveats">Caveats</a></li>
      <li><a href="#approach" id="markdown-toc-approach">Approach</a></li>
      <li><a href="#system-requirements" id="markdown-toc-system-requirements">System Requirements</a></li>
    </ul>
  </li>
  <li><a href="#tutorial" id="markdown-toc-tutorial">Tutorial</a>    <ul>
      <li><a href="#0-download-everything" id="markdown-toc-0-download-everything">0. Download Everything</a></li>
      <li><a href="#1-install-xcode-314" id="markdown-toc-1-install-xcode-314">1. Install Xcode 3.1.4</a></li>
      <li><a href="#2-force-install-the-iphone-os-313-sdk" id="markdown-toc-2-force-install-the-iphone-os-313-sdk">2. Force install the iPhone OS 3.1.3 SDK</a></li>
      <li><a href="#3-force-install-iphone-simulatorapp-from-the-iphone-os-221-sdk" id="markdown-toc-3-force-install-iphone-simulatorapp-from-the-iphone-os-221-sdk">3. Force install iPhone Simulator.app from the iPhone OS 2.2.1 SDK</a></li>
      <li><a href="#4-patch-ld" id="markdown-toc-4-patch-ld">4. Patch ld</a></li>
      <li><a href="#5-run-an-app-in-the-simulator" id="markdown-toc-5-run-an-app-in-the-simulator">5. Run an App in the Simulator</a></li>
      <li><a href="#6-coming-soon-run-an-app-on-device" id="markdown-toc-6-coming-soon-run-an-app-on-device">6. (Coming Soon) Run an App on Device</a></li>
    </ul>
  </li>
</ul>

<h2 id="overview">Overview</h2>

<p>Apple always officially required an Intel Mac to do iPhone app development, 
but I figured out how to get the iPhone OS 3.1.3 SDK installed for developing 
Original iPhone and iPhone 3G apps on your PowerPC Mac. This builds upon the
work of whoever in the community created the custom installers and scripts found
on <a href="https://www.macintoshrepository.org/1090-iphone-os-2-2-1-sdk-for-ppc">Macintosh Repository</a>.
This hack relied on the fact that even though Apple required Intel Macs for
iPhone development, they actually shipped universal binaries for the iPhone
SDK up until iPhone OS 2.2.1 SDK.</p>

<p>But the 2.2.1 SDK is quite limited and the iPhone really started to come into
its own with the 3.0 and higher SDKs. These included critical improvements
like Core Data, AVFoundation, MediaPlayer, and others. So I started to look
into whether it was possible to get the new SDK on a PowerPC Mac as well. And to my
surprise, it is possible! It will even deploy to a real iPhone running 3.1.3.</p>

<p>In this document I will walk you through how I did it.</p>

<h3 id="caveats">Caveats</h3>

<p>While this does work, there are some fundamental limitations:</p>

<ul>
  <li>iPhone Simulator only runs iPhone OS 2.2.1
    <ul>
      <li>This cannot be overcome because Apple only shipped universal PowerPC and Intel binaries for iPhone OS 2.2.1 and lower</li>
    </ul>
  </li>
  <li>You must have a developer account if you want to deploy to a device
    <ul>
      <li>There are probably workarounds, but I just did everything the official way</li>
    </ul>
  </li>
  <li>Your iPhone must be jailbroken if you want to deploy to a device
    <ul>
      <li>I think this is due to expired certificate authorities on iPhone OS 3.1.3, but I am not 100% sure</li>
    </ul>
  </li>
  <li>This Xcode 3 is very old and is not much different than the original Project Builder
    <ul>
      <li>Xcode would not get its new UI style that is still used today until Xcode 4, which only runs on Mac OS X 10.6 Snow Leopard, which is Intel-only</li>
      <li>The SDK does not support many basic features such as Automatic Reference Counting</li>
    </ul>
  </li>
</ul>

<h3 id="approach">Approach</h3>

<ol>
  <li>Install Xcode 3.1.4</li>
  <li>Force install the iPhone OS 3.1.3 SDK</li>
  <li>Force install iPhone Simulator.app from the iPhone OS 2.2.1 SDK</li>
  <li>Patch some installed files so everything runs on PowerPC</li>
</ol>

<h3 id="system-requirements">System Requirements</h3>

<ul>
  <li>PowerPC Mac</li>
  <li>Mac OS X Leopard 10.5.8 with all updates installed</li>
  <li>To run on a real device
    <ul>
      <li>USB 2.0 (it might work with USB 1.1 but no promises)</li>
      <li>Original iPhone, iPhone 3G, iPod Touch (1st generation), iPod Touch (2nd generation)</li>
      <li>Device running OS software 3.1.3 or lower
        <ul>
          <li>These devices can easily be downgraded using <a href="https://github.com/LukeZGD/Legacy-iOS-Kit">Legacy iOS Kit</a></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h2 id="tutorial">Tutorial</h2>

<h3 id="0-download-everything">0. Download Everything</h3>

<p><img src="/assets/images/retro-tech/ppc-iphone-1/download1.png" alt="AquaFox About Window" /></p>

<table>
  <thead>
    <tr>
      <th>Internet Archive</th>
      <th>Macintosh Repository</th>
      <th>All Downloaded</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-1/download2.png"><img src="/assets/images/retro-tech/ppc-iphone-1/download2-thumb.png" alt="Internet Archive" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-1/download3.png"><img src="/assets/images/retro-tech/ppc-iphone-1/download3-thumb.png" alt="Internet Archive" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-1/download4.png"><img src="/assets/images/retro-tech/ppc-iphone-1/download4-thumb.png" alt="Internet Archive" /></a></td>
    </tr>
  </tbody>
</table>

<p>If you use <a href="https://blackbirdlc.github.io/aquafox/Homepage.html">AquaFox</a>, 
you can actually do the downloads directly on your PowerPC Mac. But of course, 
you can also download everything on a modern computer and transfer it over to 
the Mac via AFP or SCP.</p>

<ol>
  <li><a href="https://archive.org/download/iOS_Firmware_Exhibit_Collection"><code class="language-plaintext highlighter-rouge">iphone_sdk_3.1.3_with_xcode_3.1.4__leopard__9m2809a.dmg</code></a> (Internet Archive)
    <ol>
      <li><a href="https://archive.org/download/iOS_Firmware_Exhibit_Collection/iphone_sdk_3.1.3_with_xcode_3.1.4__leopard__9m2809a.dmg">Direct Link</a> in case you can’t find the exact right build <code class="language-plaintext highlighter-rouge">9m2809a</code></li>
    </ol>
  </li>
  <li><a href="https://www.macintoshrepository.org/1090-iphone-os-2-2-1-sdk-for-ppc"><code class="language-plaintext highlighter-rouge">iPhone_SDK_for_iPhone_OS_2.2.1_PPC.iso</code></a> (Macintosh Repository)
    <ol>
      <li>Also available on <a href="https://macintoshgarden.org/apps/iphone-os-221-sdk-ppc">Macintosh Garden</a></li>
    </ol>
  </li>
  <li><a href="/assets/downloads/retro-tech/ppc-iphone-1/iPhone313PPCInstaller.zip"><code class="language-plaintext highlighter-rouge">iPhone313PPCInstaller.zip</code></a> (This website)
    <ol>
      <li>Do not download <code class="language-plaintext highlighter-rouge">iPhoneSDK.dist_.zip</code> or <code class="language-plaintext highlighter-rouge">iPhonePPCEnabler.dmg</code> on Macintosh Repository as these are for the 2.2.1 SDK only</li>
    </ol>
  </li>
  <li>Unzip <code class="language-plaintext highlighter-rouge">iPhone313PPCInstaller.zip</code></li>
</ol>

<h3 id="1-install-xcode-314">1. Install Xcode 3.1.4</h3>

<p><a href="/assets/images/retro-tech/ppc-iphone-1/install1.png"><img src="/assets/images/retro-tech/ppc-iphone-1/install1-thumb.png" alt="Install Xcode 3.1.4" /></a></p>

<p>Mount the <code class="language-plaintext highlighter-rouge">iphone_sdk_3.1.3_with_xcode_3.1.4__leopard__9m2809a.dmg</code> and open
the installer file to do a normal install of Xcode. I selected all possible 
options, but you should only need the basic developer tools package. The 
installer WILL NOT let you select the iPhone SDK packages. That’s OK; we will
do that in the next step. This took about 30 minutes on my 1.25 GHz iMac G4
with a 1 TB SSD, so please be patient if your system is slower and/or does not
have an SSD.</p>

<p>Note that you must install in the default location <code class="language-plaintext highlighter-rouge">/Developer</code></p>

<h3 id="2-force-install-the-iphone-os-313-sdk">2. Force install the iPhone OS 3.1.3 SDK</h3>

<p><a href="/assets/images/retro-tech/ppc-iphone-1/install2.png"><img src="/assets/images/retro-tech/ppc-iphone-1/install2-thumb.png" alt="Force Install iPhone OS 3.1.3 SDK" /></a></p>

<ol>
  <li>Verify that the iPhone SDK disk image is mounted with the correct name
    <ol>
      <li>Open Terminal and type <code class="language-plaintext highlighter-rouge">ls -al /Volumes</code></li>
      <li>You should see <code class="language-plaintext highlighter-rouge">iPhone SDK</code>, not <code class="language-plaintext highlighter-rouge">iPhone SDK 2</code> or some other name. If
it’s not called exactly <code class="language-plaintext highlighter-rouge">iPhone SDK</code> then the installer will fail.</li>
    </ol>
  </li>
  <li>Open <code class="language-plaintext highlighter-rouge">iPhone313PPCInstaller.mpkg</code> and run the installer
    <ol>
      <li>This installs the entire iPhone OS 3.1.3 SDK except for simulators higher
than 2.2.1. Those simulators are Intel only and therefore will never work
on a PowerPC Mac.</li>
    </ol>
  </li>
  <li>Unmount the iPhone SDK disk image</li>
</ol>

<p>At this point you will have a fully working Xcode install and you will even
be able to create new iPhone projects. However, there are three problems that need
to be fixed:</p>

<ol>
  <li>iPhone Simulator.app will not open because it is Intel only</li>
  <li>The copy of <code class="language-plaintext highlighter-rouge">ld</code> for the iPhone SDK is Intel only</li>
  <li>Interface Builder cannot open XIBs for the iPhone because its plugin is Intel only</li>
</ol>

<p>If you want to confirm that iPhone Simulator is Intel only you can use the 
following terminal command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ichigodaifuku:~ me<span class="nv">$ </span>lipo <span class="nt">-archs</span> /Developer/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone<span class="se">\ </span>Simulator.app/Contents/MacOS/iPhone<span class="se">\ </span>Simulator 
i386
</code></pre></div></div>

<p>You can use <code class="language-plaintext highlighter-rouge">lipo -archs</code> to check any binary on your system to see if it is
universal.</p>

<h3 id="3-force-install-iphone-simulatorapp-from-the-iphone-os-221-sdk">3. Force install iPhone Simulator.app from the iPhone OS 2.2.1 SDK</h3>

<p><a href="/assets/images/retro-tech/ppc-iphone-1/install3.png"><img src="/assets/images/retro-tech/ppc-iphone-1/install3-thumb.png" alt="Force Install iPhone OS 2.2.1 Simulator" /></a></p>

<p>One of the included files is <code class="language-plaintext highlighter-rouge">iPhone221PPCInstaller.mpkg</code>. This file was 
originally provided on Macintosh Repository but I customized it to only install
one small part of the iPhone OS 2.2.1 SDK instead of the whole thing. It only
installs the <code class="language-plaintext highlighter-rouge">iPhoneSimulatorPlatform.pkg</code>, which installs the universal binary
version of the iPhone Simulator as well as the Interface Builder plugin for
iPhone XIBs.</p>

<ol>
  <li>Unmount the iPhone SDK disk image (if you didn’t do it already as instructed in the previous step)</li>
  <li>Mount <code class="language-plaintext highlighter-rouge">iPhone_SDK_for_iPhone_OS_2.2.1_PPC.iso</code></li>
  <li>Verify that the iPhone SDK disk image is mounted with the correct name
    <ol>
      <li>Open Terminal and type <code class="language-plaintext highlighter-rouge">ls -al /Volumes</code></li>
      <li>You should see <code class="language-plaintext highlighter-rouge">iPhone SDK</code>, not <code class="language-plaintext highlighter-rouge">iPhone SDK 2</code> or some other name. If
it’s not called exactly <code class="language-plaintext highlighter-rouge">iPhone SDK</code> then the installer will fail.</li>
    </ol>
  </li>
  <li>Open <code class="language-plaintext highlighter-rouge">iPhone221PPCInstaller.mpkg</code> and run the installer. This installs:
    <ol>
      <li>iPhone Simulator app that is a universal binary</li>
      <li>Interface Builder plugin for reading iPhone XIBs that is a universal binary</li>
      <li>Runs a script that patches the xcspec files
        <ol>
          <li>There is no credit for this script on Macintosh Repository</li>
          <li>If you want to know more about what it patches, see this <a href="https://chrishardie.com/2009/01/using-the-iphoneos-sdk-on-older-ppc-macs/">blog post by Chris Hardie</a></li>
        </ol>
      </li>
    </ol>
  </li>
</ol>

<h3 id="4-patch-ld">4. Patch ld</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ichigodaifuku:~ me<span class="nv">$ </span><span class="nb">chmod</span> +x /Users/me/Desktop/iPhoneSDKInstall/iPhone313PPCldPatch.sh 
ichigodaifuku:~ me<span class="nv">$ </span><span class="nb">sudo</span> /Users/me/Desktop/iPhoneSDKInstall/iPhone313PPCldPatch.sh 
<span class="nt">---</span> iPhone OS 3.1.3 PPC Enabler LD Patch Post-Install Script Started <span class="nt">---</span>
Verifying files: SUCCESS.
Target linker <span class="o">(</span>/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ld<span class="o">)</span> and <span class="nb">source </span>linker <span class="o">(</span>/Developer/usr/bin/ld<span class="o">)</span> found.
COMMAND: Backing up Intel <span class="s1">'ld'</span> binary to /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ld.backup.intel
COMMAND: Copying functional PPC <span class="s1">'ld'</span> over the Intel one...
COMMAND: Setting executable permissions <span class="k">for</span> /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ld
SUCCESS: PPC <span class="s1">'ld'</span> copied and permissions set.
<span class="nt">---</span> iPhone OS 3.1.3 PPC Enabler LD Patch Post-Install Script Completed Successfully <span class="nt">---</span>
ichigodaifuku:~ me<span class="nv">$ </span>
</code></pre></div></div>

<p>The normal version of <code class="language-plaintext highlighter-rouge">ld</code> that comes with Xcode is a universal binary but the
version that comes with the iPhone SDK is Intel only (for some reason).
It’s the only Intel-only part of the SDK, as far as I can tell. I provided a
script to patch <code class="language-plaintext highlighter-rouge">ld</code> to make it easy, but all it does is back up the original
and then copy the version from Xcode into the iPhone SDK’s location.</p>

<p>I wanted to make the script run automatically at the end of Step 2 but I could 
not figure out how to insert the script into the installer. If you know how,
please let me know and I will improve <code class="language-plaintext highlighter-rouge">iPhone313PPCInstaller.mpkg</code>.</p>

<ol>
  <li>Open Terminal</li>
  <li>Change the permissions to make the script executable (this is not preserved by the ZIP file)
    <ol>
      <li>Type <code class="language-plaintext highlighter-rouge">chmod +x </code>; note the space at the end</li>
      <li>Drag <code class="language-plaintext highlighter-rouge">iPhone313PPCldPatch.sh</code> into the terminal window</li>
      <li>Press the return key to set the permissions</li>
    </ol>
  </li>
  <li>Execute the script with admin privileges
    <ol>
      <li>Type <code class="language-plaintext highlighter-rouge">sudo </code>; note the space at the end</li>
      <li>Drag <code class="language-plaintext highlighter-rouge">iPhone313PPCldPatch.sh</code> into the terminal window</li>
      <li>Press the return key to run the script</li>
    </ol>
  </li>
  <li>The terminal output should look like the code snippet above</li>
  <li>Restart your computer</li>
</ol>

<p>If you want to manually verify it worked, you can use the <code class="language-plaintext highlighter-rouge">lipo</code> command again:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ichigodaifuku:~ me<span class="nv">$ </span>lipo <span class="nt">-archs</span> /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ld.backup.intel
i386
ichigodaifuku:~ me<span class="nv">$ </span>lipo <span class="nt">-archs</span> /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ld
i386 ppc7400
ichigodaifuku:~ me<span class="nv">$ </span>
</code></pre></div></div>

<h3 id="5-run-an-app-in-the-simulator">5. Run an App in the Simulator</h3>

<table>
  <thead>
    <tr>
      <th>Step 2</th>
      <th>Step 3</th>
      <th>Step 4</th>
      <th>Step 5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/assets/images/retro-tech/ppc-iphone-1/test1.png"><img src="/assets/images/retro-tech/ppc-iphone-1/test1-thumb.png" alt="Test Simulator 1" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-1/test2.png"><img src="/assets/images/retro-tech/ppc-iphone-1/test2-thumb.png" alt="Test Simulator 2" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-1/test3.png"><img src="/assets/images/retro-tech/ppc-iphone-1/test3-thumb.png" alt="Test Simulator 3" /></a></td>
      <td><a href="/assets/images/retro-tech/ppc-iphone-1/test4.png"><img src="/assets/images/retro-tech/ppc-iphone-1/test4-thumb.png" alt="Test Simulator 4" /></a></td>
    </tr>
  </tbody>
</table>

<p>We can’t deploy to device yet as there is more work to do, but you should make 
a sample app and make sure it launches in the simulator.</p>

<ol>
  <li>Open Xcode and select File→New Project</li>
  <li>Choose any iPhone project you like but I chose “Tab Bar Application”</li>
  <li>There is a toolbar item labeled “Overview” where you can change the deployment target from Device to Simulator</li>
  <li>Now open one of the XIBs in Interface Builder to make sure that is working. Change some text so you know that the app runs properly when changed</li>
  <li>Click Build and Run to compile the app, launch the Simulator app, and launch your new app</li>
</ol>

<h3 id="6-coming-soon-run-an-app-on-device">6. (Coming Soon) Run an App on Device</h3>

<p>In the next episode, we will go through the steps needed to get an app running
on the device. Remember, this is before Xcode could manage signing for you, so
you will need to do it the old-fashioned way <i class="fa-solid fa-hand-peace"></i></p>]]></content><author><name></name></author><category term="Retro-Tech" /><category term="Apps" /><category term="PowerPC" /><category term="iOS" /><category term="Hobby" /><summary type="html"><![CDATA[Apple always officially required an Intel Mac to do iPhone app development, but I figured out how to get the iPhone OS 3.1.3 SDK installed for developing Original iPhone and iPhone 3G apps on your PowerPC Mac.]]></summary></entry><entry><title type="html">MathEdit for OpenStep</title><link href="https://jeffburg.com/apps/2025/10/06/MathEdit.html" rel="alternate" type="text/html" title="MathEdit for OpenStep" /><published>2025-10-06T00:00:00+00:00</published><updated>2025-10-06T00:00:00+00:00</updated><id>https://jeffburg.com/apps/2025/10/06/MathEdit</id><content type="html" xml:base="https://jeffburg.com/apps/2025/10/06/MathEdit.html"><![CDATA[<h2 id="ns-is-for-nextstep">NS is for NextStep</h2>

<p>As Cocoa developers, we were always taught that the NS in NSString, NSDictionary, NSArray,
etc., stands for NeXTSTEP. So I thought, “What is it like to build an application
for NeXTSTEP?” So I decided to try it! I got an
<a href="https://previous.nextcommunity.net">OpenStep 4.2 Virtual Machine</a> up and 
running, installed Project Builder (the precursor to Xcode) and got to work.
MathEdit is the result.</p>

<h2 id="lessons-learned">Lessons Learned</h2>

<h3 id="the-good">The Good</h3>

<p>OpenStep’s Foundation and AppKit frameworks are remarkable in that they are
extremely complete even way back in 1996 (remember Mac OS X 10.0 Cheetah was
released in 2001). If you ignore NIBs, then the code from OpenStep just works
in OS X even to this day. Yes, there are many deprecation warnings, but all
of the APIs work without issue. And yes, it means NSApplication, NSTableView,
NSWindow, NSButton, NSMenu, the responder chain, etc all work exactly the same
as they do now.</p>

<h3 id="the-bad">The Bad</h3>

<p>However, there is a lot of core functionality missing. Networking for example.
NSData will download things for you when you give it an internet path, but
this has all the pitfalls of modern NSData. It’s single-threaded, blocking, has no
progress, etc. If you want anything more than that you need to drop down to 
BSD Sockets. AppKit is missing basics like NSToolbar and NSDocument. Foundation
is missing basics like NSError and NSURL, which kind of explains why there
are all those duplicate methods with NSURL and NSString and with NSError and
without NSError.</p>

<h3 id="the-ugly">The Ugly</h3>

<p>Lastly, there is the really hard stuff. Objective-C before “2.0” as Apple called
it is pretty painful. That said, Objective-C 2.0 did not come to Mac OS X until
10.7, so that’s pretty late in OS X history. But still, we are talking about
the most basic features missing:</p>

<ul>
  <li>Automatic Reference Counting</li>
  <li><a href="https://fuckingblocksyntax.com/">Fucking Block Syntax</a></li>
  <li>Property Syntax</li>
  <li>Dot Syntax</li>
  <li>Grand Central Dispatch</li>
  <li>Collection Literals</li>
</ul>

<p>The other major pain point is Nibs. While OpenStep Nibs do work in early 
versions of OS X, Apple had a bad habit of changing the Nib format, XIB format,
and then just refusing to load older Nibs for security reasons over the years.
So the only way to make an app that is compatible across history is to not use
Nibs… and unlike on iOS, AppKit really does not like this. I spent a lot of
time getting memory management to work properly in early versions of OS X
that just ABSOLUTELY ASSUMED you were using Nibs. I did get it to work, but this
was like days and days and days of research. Also, I had to learn 
<a href="https://lapcatsoftware.com/blog/2007/06/17/working-without-a-nib-part-4-setapplemenu/">the secret trick</a>
to set the Application Menu which did not exist in OpenStep.</p>

<h2 id="features">Features</h2>

<p>MathEdit is basically like Math Notes from Apple. It’s a normal text editor
like TextEdit, but if you type in a mathematical formula in the text, it 
automatically parses it and solves it for you. It makes extensive use of
NSDecimalNumber and NSTextAttachment to do this. NSTextAttachment in particular
is the star of the show. It still always shocks me that this was missing from 
iOS until iOS 7 even though it was in Mac OS X and even OpenStep before that.
It is such a powerful way to enhance your text documents.</p>

<h2 id="caveats">Caveats</h2>

<p>I honestly don’t use MathEdit all that often. It is on all my Macs including
my vintage ones and I think it is a cool proof of concept. But I don’t think it’s
much more than that. But if you want to try it out for yourself, grab a build
for your favorite Mac and give it a try.</p>

<p><a href="https://github.com/jeffreybergier/MathEdit">https://github.com/jeffreybergier/MathEdit</a></p>]]></content><author><name></name></author><category term="Apps" /><category term="Apple" /><category term="Retro-Tech" /><category term="OpenStep" /><category term="Objective-C" /><category term="Cocoa" /><category term="Mac-OS-X" /><category term="PowerPC" /><summary type="html"><![CDATA[Imagine Apple's Math Notes but built as a traditional Cocoa application with the unusual added feature that its codebase spans 30 years of Mac OS X history.]]></summary></entry><entry><title type="html">Retro Software Update Server</title><link href="https://jeffburg.com/retro-tech/2025/09/09/Retro-Software-Update-Server.html" rel="alternate" type="text/html" title="Retro Software Update Server" /><published>2025-09-09T00:00:00+00:00</published><updated>2025-09-09T00:00:00+00:00</updated><id>https://jeffburg.com/retro-tech/2025/09/09/Retro-Software-Update-Server</id><content type="html" xml:base="https://jeffburg.com/retro-tech/2025/09/09/Retro-Software-Update-Server.html"><![CDATA[<p>A guide on setting up an Apple Software Update Server mirror in case Apple ever
decides to shut down these old update servers. So far, Software Update still
works on Mac OS X 10.4 and above, but Apple already shut down servers for 10.3,
10.2, 10.1, and 10.0 and those can’t be recovered.</p>

<p>If you have an interest in retro Macs, this may be something you would like to
set up in case something bad were to happen. This approach works because these
servers are still online and still work. If you happen to be reading this 10
years in the future (2035) because Apple shut down the software update servers
for PowerPC Macs, then it’s too late. Sorry.</p>

<h2 id="table-of-contents">Table of Contents</h2>

<ul id="markdown-toc">
  <li><a href="#table-of-contents" id="markdown-toc-table-of-contents">Table of Contents</a></li>
  <li><a href="#who-am-i" id="markdown-toc-who-am-i">Who Am I</a></li>
  <li><a href="#how-do-software-updates-work-on-the-mac" id="markdown-toc-how-do-software-updates-work-on-the-mac">How Do Software Updates Work on the Mac?</a></li>
  <li><a href="#approach--guide--table-of-contents" id="markdown-toc-approach--guide--table-of-contents">Approach / Guide / Table of Contents</a></li>
  <li><a href="#system-requirements" id="markdown-toc-system-requirements">System Requirements</a></li>
  <li><a href="#guide" id="markdown-toc-guide">Guide</a>    <ul>
      <li><a href="#1-install-git-homebrew-python-2" id="markdown-toc-1-install-git-homebrew-python-2">1. Install Git, Homebrew, Python 2</a>        <ul>
          <li><a href="#python-2" id="markdown-toc-python-2">Python 2</a></li>
        </ul>
      </li>
      <li><a href="#2-choose-where-software-updates-will-be-mirrored-on-your-server-mac" id="markdown-toc-2-choose-where-software-updates-will-be-mirrored-on-your-server-mac">2. Choose where Software Updates will be mirrored on your server Mac</a></li>
      <li><a href="#3-configure-the-web-server-on-your-server-mac" id="markdown-toc-3-configure-the-web-server-on-your-server-mac">3. Configure the Web Server on your server Mac</a></li>
      <li><a href="#4-configure-reposado-on-your-server-mac" id="markdown-toc-4-configure-reposado-on-your-server-mac">4. Configure Reposado on your server Mac</a></li>
      <li><a href="#5-use-reposado-to-mirror-apples-existing-software-updates-to-your-server-mac" id="markdown-toc-5-use-reposado-to-mirror-apples-existing-software-updates-to-your-server-mac">5. Use Reposado to mirror Apple’s existing software updates to your server Mac</a></li>
      <li><a href="#6-configure-the-retro-mac-to-fetch-updates-from-your-server-mac" id="markdown-toc-6-configure-the-retro-mac-to-fetch-updates-from-your-server-mac">6. Configure the retro Mac to fetch updates from your server Mac</a></li>
    </ul>
  </li>
</ul>

<h2 id="who-am-i">Who Am I</h2>

<p>I’m Jeff, a fan of Mac OS X, NeXT, a software developer, and general retro tech
enthusiast. I have other retro tech guides as well as software I have developed
for retro Macs, so please check it out.</p>

<ul>
  <li><a href="https://github.com/jeffreybergier/MathEdit">MathEdit for OpenStep</a>
    <ul>
      <li><a href="https://jeffburg.social/tags/OpenStep">MathEdit Development Posts on my Mastodon</a></li>
    </ul>
  </li>
  <li><a href="/retro-tech/2025/08/17/Retro-Stream-Tutorial.html">Retro Stream Tutorial</a></li>
  <li><a href="https://jeffburg.social/tags/iMacG4">iMac G4 Posts on my Mastodon</a></li>
  <li><a href="https://jeffburg.social/tags/iMac5K">Homemade iMac 5K Monitor posts on my Mastodon</a></li>
</ul>

<h2 id="how-do-software-updates-work-on-the-mac">How Do Software Updates Work on the Mac?</h2>

<p><img src="/assets/images/retro-tech/software-update-server/002-SUS.png" alt="Retro Software Update Server" /></p>

<p>The software update system for Mac OS X 10.4 and higher is actually amazingly
simple and flexible. For 10.3 and lower, it was a traditional client/server
application. The client would send some information about itself to the server
and the server would process this and tell the client what updates it needed.
While this seems logical, it also means your servers will be busy processing
requests from clients. And as Apple started to become really successful at
selling Macs in the early 2000s, the OS X team probably realized that this would
fail to scale.</p>

<p>So what did they change? Well, in 10.4 and later, the Software Update Server is
nothing more than a huge set of files and a few Update catalogs… one for each
version of the OS. These sucatalog files list every update available as well
as “distribution” files. These distributions are available for every supported
language. They contain the localized description, but they also contain inline
JavaScript that will be executed by the client to determine if the update is
needed.</p>

<p>What this means is Apple offloaded all of the work of determining which updates
it needs from the server to the client. This is great for Apple’s server load,
but it also explains why checking for updates can take so long the first time.
The client literally needs to download hundreds of distribution files. Yes, they
are only a few K each, but then the client also needs to execute the JavaScript
for each one.</p>

<p>The other side effect of this system is that now it is easy to mirror the
software update server as there is no server-side logic.</p>

<p><strong>Enter Mac OS X Server Software Update Server</strong></p>

<p>Back in the day, Apple included a Software Update Server as part of Mac OS X
Server. Apple intended companies to use this to mirror the software updates
internally in a company to do 2 things:</p>

<ol>
  <li>Save bandwidth when updating a fleet of Macs</li>
  <li>Control which updates were
available to the Macs in your company to ensure that you could test
compatibility first</li>
</ol>

<p>This feature is long dead and now Apple facilitates the same control by allowing
companies to whitelist or blacklist updates via MDM (Mobile Device Management).
This does not create a mirror, but simply tells client Macs to ignore updates.
This method does not save bandwidth in your company. But in the modern day,
bandwidth is a lot less expensive than it used to be.</p>

<p>When I first started this project, I actually tried to use 10.6 Snow Leopard
Server to host the software update server. However, after much troubleshooting,
I could not get it to download the updates reliably. It would only download
about 1/3 of the updates which is not enough for success. I don’t know why it
refused to download them. All it said was there was a suspected security problem
and so it refused. ChatGPT suggested that the Apple Software Update Server
distributed with their OS had very picky client-side URL validation and if the
URLs failed these basic checks, it would not even start the download.</p>

<p><strong>Enter <a href="https://github.com/wdas/reposado/blob/main/docs/getting_started.md">Reposado</a></strong></p>

<p>Reposado is an open-source project written in Python 2 that replicates the
functionality of the software update server. It checks the Apple catalogs,
downloads the updates, and then generates new catalogs specific to your mirror.
In my experience doing this, it seems to work super well. So this guide will
explain how to set this up for you so you can prepare for the very dark day when
Apple shuts down these old software updates (which may never come).</p>

<h2 id="approach--guide--table-of-contents">Approach / Guide / Table of Contents</h2>

<ol>
  <li>Install Git, Homebrew, Python 2</li>
  <li>Choose where Software Updates will be mirrored on your server Mac</li>
  <li>Configure the Web Server on your server Mac</li>
  <li>Configure Reposado on your server Mac</li>
  <li>Use Reposado to mirror Apple’s existing software updates to your server Mac</li>
  <li>Configure the retro Mac to fetch updates from your server Mac</li>
</ol>

<h2 id="system-requirements">System Requirements</h2>

<p><strong>Server Mac</strong></p>

<ol>
  <li>50 GB+ to dedicate to storing updates
    <ul>
      <li>Even the minimal set for 10.4 Tiger is 20 GB</li>
      <li>Supporting all the way to 10.9 Mavericks takes 200 GB</li>
      <li>ChatGPT says that around 10.11/10.12 Apple started locking down the 
software update server and it can’t be changed</li>
    </ul>
  </li>
  <li>A normal home network
    <ul>
      <li>This guide assumes you connect all your Macs to the internet with Ethernet
or Wi-Fi and they can find each other and connect to each other using Bonjour
names.</li>
    </ul>
  </li>
</ol>

<p><strong>Retro Mac</strong></p>

<ol>
  <li>Connected to Ethernet or Wi-Fi on the same network as your server Mac</li>
  <li>Mac OS X 10.4 or higher</li>
</ol>

<p>Note that the server Mac can really be any system. Even modern Linux (such as 
Debian and Ubuntu) automatically identify themselves via Bonjour names. But
this guide gives instructions when using a Mac as the server.</p>

<h2 id="guide">Guide</h2>

<p>This guide pretty much runs 100% from Terminal. 
Any text <code class="language-plaintext highlighter-rouge">in this special format</code> is a terminal command you can copy and paste.</p>

<h3 id="1-install-git-homebrew-python-2">1. Install Git, Homebrew, Python 2</h3>

<ul>
  <li>Git is included as part of the Xcode command line utilities AND these utilities
are required for Homebrew anyway.</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">xcode-select --install</code></p>

<ul>
  <li>Homebrew is a simple package manager for macOS. It’s easy to install.
Just follow the guide on <a href="https://brew.sh">https://brew.sh</a>. Make sure to 
follow the instructions in the Terminal after installing Homebrew.
They are required to make sure that commands you install via Homebrew are
easily launchable from the Terminal.</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"</code></p>

<h4 id="python-2">Python 2</h4>

<p>This is the difficult one. Python 2 is far out of date, and Homebrew no longer
has an easy installer for it. We have to use pyenv, and this basically seemed
to build Python 2 from source, which is never fun.</p>

<p><strong>Install pyenv and other tools needed for compiling</strong></p>

<p><code class="language-plaintext highlighter-rouge">brew install pyenv zlib bzip2 readline openssl@1.1</code></p>

<p><strong>Configure the environment for the Python 2 build</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export LDFLAGS="-L$(brew --prefix zlib)/lib -L$(brew --prefix bzip2)/lib -L$(brew --prefix readline)/lib -L$(brew --prefix openssl@1.1)/lib"
export CPPFLAGS="-I$(brew --prefix zlib)/include -I$(brew --prefix bzip2)/include -I$(brew --prefix readline)/include -I$(brew --prefix openssl@1.1)/include"
export PKG_CONFIG_PATH="$(brew --prefix zlib)/lib/pkgconfig:$(brew --prefix bzip2)/lib/pkgconfig:$(brew --prefix readline)/lib/pkgconfig:$(brew --prefix openssl@1.1)/lib/pkgconfig"
</code></pre></div></div>

<p><strong>Configure your shell to use pyenv</strong></p>

<p>Sorry, you may need to look up the commands
for how to use <code class="language-plaintext highlighter-rouge">vi</code> or use a text editor you know better, like <code class="language-plaintext highlighter-rouge">nano</code>.</p>

<p><code class="language-plaintext highlighter-rouge">vi ~/.zshrc</code></p>

<p>Paste in the following text and then save and exit:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
</code></pre></div></div>

<p><strong>Compile Python 2</strong></p>

<p><code class="language-plaintext highlighter-rouge">pyenv install 2.7.18</code></p>

<p><strong>Set Python 2 as the default</strong></p>

<p><code class="language-plaintext highlighter-rouge">pyenv global 2.7.18</code></p>

<p><strong>Test Python</strong></p>

<p>You should not get an error when running this command:</p>

<p><code class="language-plaintext highlighter-rouge">python --version</code></p>

<h3 id="2-choose-where-software-updates-will-be-mirrored-on-your-server-mac">2. Choose where Software Updates will be mirrored on your server Mac</h3>

<p>The Software Update application on your Mac expects a very specific directory
structure and catalog format. Reposado creates this for you. But you still
need to decide where on your system this will go and you will also need
to configure the web server to use this directory. It is OK to have this on
an external drive as the mirror takes a lot of space.</p>

<p>On my system I put everything in</p>

<p><code class="language-plaintext highlighter-rouge">/Volumes/Data/Virtualization/SUS</code></p>

<p>So the web server document root and Reposado storage are</p>

<p><code class="language-plaintext highlighter-rouge">/Volumes/Data/Virtualization/SUS/www</code></p>

<p>The Reposado Git clone is</p>

<p><code class="language-plaintext highlighter-rouge">/Volumes/Data/Virtualization/SUS/reposado</code></p>

<p>The Reposado metadata storage is</p>

<p><code class="language-plaintext highlighter-rouge">/Volumes/Data/Virtualization/SUS/reposado/code/metadata</code></p>

<p>So you can put everything wherever you like, but this guide assumes this
structure.</p>

<h3 id="3-configure-the-web-server-on-your-server-mac">3. Configure the Web Server on your server Mac</h3>

<p>Mac OS X has always included Apache as a web server; it’s just disabled by
default. It’s nothing fancy, but it will work for our retro Mac.</p>

<p><strong>Set DocumentRoot to the directory you selected above</strong></p>

<p><code class="language-plaintext highlighter-rouge">sudo vi /etc/apache2/httpd.conf</code></p>

<p>This file is pretty long, so you have to scroll down to find the DocumentRoot
setting. I don’t think you need to change other options, but here are the key
ones.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DocumentRoot "/Volumes/Data/Virtualization/SUS/www"
&lt;Directory "/Volumes/Data/Virtualization/SUS/www"&gt;
    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted
&lt;/Directory&gt;
</code></pre></div></div>

<p><strong>Give the web server full disk access</strong></p>

<p>This might not be needed if your directory root is on the boot drive,
but I definitely needed it because mine is on an external drive.</p>

<ol>
  <li>Open System Settings→Privacy→Full Disk Access</li>
  <li>Click the plus button and enter your admin password</li>
  <li>Press ⌘+Shift+G in the file picker and type <code class="language-plaintext highlighter-rouge">/usr/sbin/httpd</code></li>
</ol>

<p><strong>Turn on the Web Server</strong></p>

<p><code class="language-plaintext highlighter-rouge">sudo launchctl load -w /System/Library/LaunchDaemons/org.apache.httpd.plist</code></p>

<p><strong>Test the Web Server</strong></p>

<p>Put a test file in the document root. It can be any file, but perhaps a small
<code class="language-plaintext highlighter-rouge">.txt</code> file is easiest.</p>

<ol>
  <li>Open Safari on your Server Mac and type <code class="language-plaintext highlighter-rouge">http://localhost/mytestfile.txt</code></li>
  <li>Open Safari on your Retro Mac and type <code class="language-plaintext highlighter-rouge">http://Server-Bonjour-Name.local/mytestfile.txt</code></li>
</ol>

<p>Note that step 2 is critical. If you can’t get the website to load on your
Retro Mac you won’t be able to update from it either.</p>

<h3 id="4-configure-reposado-on-your-server-mac">4. Configure Reposado on your server Mac</h3>

<p>OK, Reposado is where all the magic happens, so let’s get started!</p>

<p><strong>Clone the <a href="https://github.com/wdas/reposado">Reposado</a> Repository</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd /Volumes/Data/Virtualization/SUS
git clone https://github.com/wdas/reposado.git
</code></pre></div></div>

<p><strong>Configure Reposado</strong></p>

<p><code class="language-plaintext highlighter-rouge">/Volumes/Data/Virtualization/SUS/reposado/code/repoutil --configure</code></p>

<p>Reposado will ask you for the directories you chose earlier. Note if you drag in
the directories from the Finder, it appends a space after them and the developer
of Reposado says this will cause errors.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Filesystem path to store replicated catalogs and updates [None]: /Volumes/Data/Virtualization/SUS/www
Filesystem path to store Reposado metadata [None]: /Volumes/Data/Virtualization/SUS/reposado/code/metadata 
Base URL for your local Software Update Service
(Example: http://su.your.org -- leave empty if you are not replicating updates) [None]: Server-Bonjour-Name.local
</code></pre></div></div>

<p><strong>Configure Reposado (Continued)</strong></p>

<p>Reposado defaults to being a production software update server mirror, which is
not what you want because you are setting up a retro software update mirror in
case of catastrophe. And you probably don’t want to take 400+ GB of updates for
all modern Macs when you could use less than 100 GB to support updating 10.6,
10.5, and 10.4 only.</p>

<p><code class="language-plaintext highlighter-rouge">open /Volumes/Data/Virtualization/SUS/reposado/code/preferences.plist</code> (TextEdit should open)</p>

<p>Your configuration file will look something like this. Copy and paste the keys
listed below into your configuration and then customize them as you like:</p>

<ul>
  <li><strong>AppleCatalogURLs</strong> to the bare minimum for 10.4 Tiger only (we will expand later after we confirm it’s working)</li>
  <li><strong>PreferredLocalizations</strong> to include the languages you care about</li>
  <li><strong>RepoSyncLogFile</strong> so you can see sync progress and verify everything is working</li>
</ul>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="cp">&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;</span>
<span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">&gt;</span>
<span class="nt">&lt;dict&gt;</span>
	<span class="nt">&lt;key&gt;</span>LocalCatalogURLBase<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;string&gt;</span>http://Barely-Regal.local<span class="nt">&lt;/string&gt;</span>
	<span class="nt">&lt;key&gt;</span>UpdatesMetadataDir<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;string&gt;</span>/Volumes/Data/Virtualization/SUS/reposado/code/metadata<span class="nt">&lt;/string&gt;</span>
	<span class="nt">&lt;key&gt;</span>UpdatesRootDir<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;string&gt;</span>/Volumes/Data/Virtualization/SUS/www<span class="nt">&lt;/string&gt;</span>
	<span class="nt">&lt;key&gt;</span>AppleCatalogURLs<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;array&gt;</span>
    <span class="nt">&lt;string&gt;</span>http://swscan.apple.com/content/catalogs/index.sucatalog<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;string&gt;</span>http://swscan.apple.com/content/catalogs/index-1.sucatalog<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;/array&gt;</span>
    <span class="nt">&lt;key&gt;</span>PreferredLocalizations<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;array&gt;</span>
    <span class="nt">&lt;string&gt;</span>English<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;string&gt;</span>en<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;string&gt;</span>Japanese<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;string&gt;</span>ja<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;/array&gt;</span>
    <span class="nt">&lt;key&gt;</span>RepoSyncLogFile<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>/var/log/reposado_sync.log<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>HumanReadableSizes<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p><strong>Create the Log File</strong></p>

<p>Create the log file and make it readable and writable by all users. Normally it
would be better to make it readable and writable by the right people, but it’s a
single log file. Not really security-critical.</p>

<p><code class="language-plaintext highlighter-rouge">sudo touch /var/log/reposado_sync.log</code>
<code class="language-plaintext highlighter-rouge">sudo chmod 777 /var/log/reposado_sync.log</code></p>

<p><strong>You’re Ready to Roll!</strong></p>

<p>In the next section we will fire this thing up and updates will start to stream
in!</p>

<h3 id="5-use-reposado-to-mirror-apples-existing-software-updates-to-your-server-mac">5. Use Reposado to mirror Apple’s existing software updates to your server Mac</h3>

<p>First we will do a test run with the minimal set of Software Update Catalogs
that you configured in the previous section. This is about 20–25 GB worth of
updates, so depending on how fast or slow your internet connection is, this
could take a while. Reposado does not generate the catalogs for your retro Mac
to use until it has finished downloading everything, so you need to wait until
it finishes. This is why we start with the minimal catalogs.</p>

<p><strong>Tail the log so you can see the progress</strong></p>

<p>Open a new terminal window or tab and run this command to view the log as it
gets updated.</p>

<p><code class="language-plaintext highlighter-rouge">tail -f /var/log/reposado_sync.log</code></p>

<p><strong>Start the Software Update Sync</strong></p>

<p><code class="language-plaintext highlighter-rouge">/Volumes/Data/Virtualization/SUS/reposado/code/repo_sync</code></p>

<p>That’s it. Now you have to wait for it to download 20 GB or so. You should be able
to see progress in the other terminal window with the tailed log file.</p>

<p><strong>What Success Looks Like</strong></p>

<p>The log file will output hundreds of lines, so you may be wondering what success
looks like. After downloading all of the files, Reposado will build the catalog
files and then report <code class="language-plaintext highlighter-rouge">repo_sync run ended</code>. It will look something like this.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Sep 07 17:26:44 Could not get data from dist file: /Volumes/Data/Virtualization/SUS/content/downloads/03/09/061-5359/mxWDL8cXkFTv9pCp8GMFr2bJFXjtqyQbvx/022-4184.English.dist
Sep 07 17:26:46 Could not replicate http://swcdn.apple.com/content/downloads/49/33/061-6390/BYBbD3TFbdpntDts78ddSzDSRpXnGKnQsy/FinalCutPro6.0.6Update.smd: Error 22: The requested URL returned error: 403
Sep 07 17:26:51 Building index.sucatalog...
Sep 07 17:26:51 WARNING: did not add product 061-1688 to catalog index.sucatalog.apple because it has not been downloaded.
Sep 07 17:26:51 WARNING: did not add product 061-2736 to catalog index.sucatalog.apple because it has not been downloaded.
Sep 07 17:26:51 WARNING: did not add product 061-1720 to catalog index.sucatalog.apple because it has not been downloaded.
Sep 07 17:26:51 WARNING: did not add product 041-85067 to catalog index.sucatalog.apple because it has not been downloaded.
Sep 07 17:26:51 WARNING: did not add product 061-6390 to catalog index.sucatalog.apple because it has not been downloaded.
Sep 07 17:26:51 WARNING: did not add product 061-2006 to catalog index.sucatalog.apple because it has not been downloaded.
Sep 07 17:26:51 WARNING: did not add product 061-5359 to catalog index.sucatalog.apple because it has not been downloaded.
Sep 07 17:26:51 WARNING: did not add product 061-2193 to catalog index.sucatalog.apple because it has not been downloaded.
Sep 07 17:26:52 178 products found in http://swscan.apple.com/content/catalogs/index-1.sucatalog
Sep 07 17:26:53 Building index-1.sucatalog...
Sep 07 17:26:54 268 products found in http://swscan.apple.com/content/catalogs/others/index-leopard.merged-1.sucatalog
Sep 07 17:26:55 Building index-leopard.merged-1.sucatalog...
Sep 07 17:26:56 345 products found in http://swscan.apple.com/content/catalogs/others/index-leopard-snowleopard.merged-1.sucatalog
Sep 07 17:27:05 Building index-leopard-snowleopard.merged-1.sucatalog...
Sep 07 17:27:08 600 products found in http://swscan.apple.com/content/catalogs/others/index-lion-snowleopard-leopard.merged-1.sucatalog
Sep 07 17:27:37 Building index-lion-snowleopard-leopard.merged-1.sucatalog...
Sep 07 17:27:38 448 products found in http://swscan.apple.com/content/catalogs/others/index-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog
Sep 07 17:27:42 Building index-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog...
Sep 07 17:27:43 464 products found in https://swscan.apple.com/content/catalogs/others/index-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog
Sep 07 17:27:57 Building index-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog...
Sep 07 17:27:58 repo_sync run ended
</code></pre></div></div>

<p>Warnings will be normal as some updates will not be available any longer. If the
number of products not downloaded is HUGE—say, hundreds of items—then you have
a problem. But I think 10 or 20 is normal?</p>

<p><strong>Choose Max Version of OS X You Want Updates For</strong></p>

<p>This is a personal choice based on what Macs you are interested in, how much
space you have to dedicate to this, and what Macs you think Apple will remove
from the update servers soonest.</p>

<p>You can see all the catalogs in this <a href="https://github.com/wdas/reposado/blob/main/docs/reposado_preferences.md">Reposado file</a>.
They are named in a pretty orderly way, so select which OS you want to support
and then put in the catalog file for that one and all the versions lower. So if
you want to support 10.10 Yosemite and lower, your Preferences.plist would look
like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;key&gt;AppleCatalogURLs&lt;/key&gt;
&lt;array&gt;
    &lt;string&gt;http://swscan.apple.com/content/catalogs/index.sucatalog&lt;/string&gt;
    &lt;string&gt;http://swscan.apple.com/content/catalogs/index-1.sucatalog&lt;/string&gt;
    &lt;string&gt;http://swscan.apple.com/content/catalogs/others/index-leopard.merged-1.sucatalog&lt;/string&gt;
    &lt;string&gt;http://swscan.apple.com/content/catalogs/others/index-leopard-snowleopard.merged-1.sucatalog&lt;/string&gt;
    &lt;string&gt;http://swscan.apple.com/content/catalogs/others/index-lion-snowleopard-leopard.merged-1.sucatalog&lt;/string&gt;
    &lt;string&gt;http://swscan.apple.com/content/catalogs/others/index-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog&lt;/string&gt;
    &lt;string&gt;https://swscan.apple.com/content/catalogs/others/index-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog&lt;/string&gt;
    &lt;string&gt;https://swscan.apple.com/content/catalogs/others/index-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog&lt;/string&gt;
&lt;/array&gt;
</code></pre></div></div>

<p>After you add the new catalogs, run sync again:</p>

<p><code class="language-plaintext highlighter-rouge">/Volumes/Data/Virtualization/SUS/reposado/code/repo_sync</code></p>

<p>Note that supporting up to 10.10 Yosemite takes about 300 GB of space.</p>

<p><img src="/assets/images/retro-tech/software-update-server/003-Reposado.png" alt="Retro Software Update Server" /></p>

<p><strong>Some Final Things</strong></p>

<p>Basically, after you complete the sync, you never need to run Reposado again. The
web server and the <code class="language-plaintext highlighter-rouge">www</code> folder are all you need. The Software Update server is
based on a completely static system, which is super nice and reliable.</p>

<p>If you change the hostname of the server Mac, you can update the
Preferences.plist file and then run sync again, and it won’t download any new
files, but at the end Reposado will update the catalog files to use your new
hostname which is fantastic.</p>

<h3 id="6-configure-the-retro-mac-to-fetch-updates-from-your-server-mac">6. Configure the retro Mac to fetch updates from your server Mac</h3>

<p>So this is a bit tricky, because it’s likely that your retro Mac(s) have already
run all of their software updates. So when you tell them to check for updates
from your new Software Update Server Mirror, the retro Mac will still tell you
there are no updates available. So how do you test?</p>

<ul>
  <li>If you don’t mind erasing your retro Mac, you could just reinstall the OS.</li>
  <li>Install a fresh OS to an external FireWire hard drive and boot from that.</li>
  <li>Install an old version of OS X in a virtual machine and try that.</li>
</ul>

<p><strong>Configure the Retro Mac</strong></p>

<p>All of these have their pros and cons so you decide. But once you get the Mac
working and it is ready for software updates, run the following command in the
Terminal on the retro Mac:</p>

<p><code class="language-plaintext highlighter-rouge">sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate CatalogURL "http://Server-Bonjour-Name.local/content/catalogs/others/index-lion-snowleopard-leopard.merged-1.sucatalog"</code></p>

<p>Note that you have to point the OS at the correct catalog given the version of
OS X you are running or else it won’t see all of the updates.</p>

<p><strong>Tail the Apache Access Logs on the Server Mac</strong></p>

<p>Tailing these logs will show you that the retro Mac is checking all the updates.</p>

<p><code class="language-plaintext highlighter-rouge">tail -f /var/log/apache2/access_log /var/log/apache2/error_log</code></p>

<p><strong>Check for Updates on the Retro Mac</strong></p>

<p>Check for updates on your retro Mac and install them. If it’s a fresh install, it
may take several reboots to get through all the updates.</p>

<p><code class="language-plaintext highlighter-rouge">Apple Menu→Software Update</code></p>

<p><strong>What Success Looks Like</strong></p>

<p>In the logs on the server Mac, you should see hundreds of file requests to check
for updates. On the retro Mac, in the title of the Software
Update window you should see the hostname of your server Mac in parentheses. See
the screenshot below.</p>

<p><img src="/assets/images/retro-tech/software-update-server/001-Title.png" alt="Retro Mac Configured" /></p>

<p><strong>Some Final Things</strong></p>

<p>In the real software update server, Apache rewrite rules are used so any version
can fetch index.sucatalog and the server will smartly rewrite the URL to be the
correct one. You can add these if you like, but since you’re probably not
running an army of retro Macs, it’s probably easier to just do it on the retro
Mac instead. Here are the rewrite rules if you are interested.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} Darwin/9
RewriteRule ^/index\.sucatalog$ /index-leopard.merged-1.sucatalog
RewriteCond %{HTTP_USER_AGENT} Darwin/10
RewriteRule ^/index\.sucatalog$ /index-leopard-snowleopard.merged-1.sucatalog
RewriteCond %{HTTP_USER_AGENT} Darwin/11
RewriteRule ^/index\.sucatalog$ /index-lion-snowleopard-leopard.merged-1.sucatalog
</code></pre></div></div>]]></content><author><name></name></author><category term="Retro-Tech" /><category term="PowerPC" /><category term="Mac-OS-X" /><category term="Software-Update" /><category term="Hobby" /><summary type="html"><![CDATA[A guide to setting up an Apple Software Update Server mirror in case Apple ever shuts down the old Mac OS X update servers.]]></summary></entry></feed>