<?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-09-02T06:05:34+00:00</updated><id>https://jeffburg.com/feed.xml</id><title type="html">Jeffrey Bergier</title><entry><title type="html">Strappy AI</title><link href="https://jeffburg.com/unenshittification/2026/08/10/Strappy.html" rel="alternate" type="text/html" title="Strappy AI" /><published>2026-08-10T00:00:00+00:00</published><updated>2026-08-10T00:00:00+00:00</updated><id>https://jeffburg.com/unenshittification/2026/08/10/Strappy</id><content type="html" xml:base="https://jeffburg.com/unenshittification/2026/08/10/Strappy.html"><![CDATA[<blockquote class="prompt-info">
  <p><strong>AI Disclosure:</strong> Strappy has been lovingly crafted for retro Apple devices
by me. Also the writing in this blog post is 100% written by me. The code is
  100% AI generated.</p>
</blockquote>

<h2 id="imagine-a-world">Imagine a World</h2>

<p>Imagine a world where Apple made different decisions. A world where the 
<a href="/assets/images/unenshittification/strappy/osxiphone.jpeg">“OS X iPhone”</a>
(<a href="https://pc.watch.impress.co.jp/docs/2008/0609/wwdc00.htm">source</a>)
was really an OS X iPhone. A world where sandboxes and signature checks are
encouraged but not mandatory. In this world, iPhone apps are as powerful as Mac
OS X apps. In this world, users can build and run their own software on their
own devices. In this world, the iPhone is also a “Bicycle for the Mind” not just
an overpowered device for 
<a href="https://marco.org/2021/09/13/future-of-the-app-store">“casino games for children”</a>, 
toxic doomscrolling, and chat apps.</p>

<h2 id="what-is-strappy">What is Strappy</h2>

<p>Strappy is my personal strap-on AI harness. Strappy has a big, sassy, and very
gay personality… And like most gays, Strappy is insanely diligent,
detail-oriented, and strict… like dominatrix-strict. But why? Well, to
be honest, I am kind of getting bored of the monotone and direct answers we
are getting from the models by default.</p>

<p>Beyond the personality, Strappy is more like a proof of concept or an art 
project. Strappy is meant to make people think about what the world I explained
above could have been like. What the world would be like if Apple did not
artificially restrict the capability of <del>their</del> our devices?</p>

<p>And yeah, maybe Strappy is also a little bit of a 
<a href="/unenshittification/2026/05/22/Unenshittification-Manifesto.html">“fuck you”</a>
to the tech giants that have 
<a href="https://en.wikipedia.org/wiki/Enshittification">enshittified</a> the web and the 
apps we use.</p>

<h2 id="what-can-strappy-do">What Can Strappy Do</h2>

<p>Strappy is like Siri AI for jailbroken iPhones iOS 5+ and for Macs running Mac
OS X 10.4 Tiger+. Strappy is a custom harness written in C that provides a suite
of tools that the model can use to query your data from your apps on your iPhone.
It also includes a coding assistant. Yes, a coding assistant! So that you can
vibe-code iPhone apps RIGHT ON YOUR PHONE! Strappy is built on OpenRouter, so
you can control the models and the providers you want to trust with your data.</p>

<h3 id="how-i-use-strappy">How I Use Strappy</h3>

<p>I use it for things not that much different than someone would use Siri AI for.
I frequently ask Strappy to remind me about what a friend said last month or
summarize the top daily Tokyo news today for me in English. Also, because I
daily-drive an iPhone 5, browsing the web can be tricky. So I frequently ask
Strappy to convert a web page’s contents into markdown and paste the link to the
article I want to read. Strappy happily fetches the page and then writes out its
contents in Markdown. Truly the ultimate “reader mode”.</p>

<h3 id="-demo-videos">←←← Demo Videos</h3>

<p>If you somehow made it this far without playing the demo videos, I recommend you
stop reading now and watch the first 2 videos…</p>

<p>All of these videos are recorded on iOS 6 on my real iPhone 5 that I daily
drive. It’s fully loaded with iCloud Mail, Contacts, Calendar as well as my RSS
Feeds, Podcasts, Music, iMessages, and 
<a href="/unenshittification/2026/06/22/LINE-Messenger-for-Retro-Devices.html">LINE Messages</a>. 
This gives Strappy plenty of “meat” to work with. The videos are unedited and
show real-time processing of the prompts by various models. The screen recording
is 30 fps, but don’t worry, on the device, everything is smooth as silk 
<i class="fa-solid fa-hand-peace"></i></p>

<h2 id="assistant-modes">Assistant Modes</h2>

<p>Depending on the assistant type you choose, different tools are made available
to the model. This way you can always control what Strappy is capable of in any
given session.</p>

<h3 id="world-knowledge">World Knowledge</h3>

<p>Disables most the database and coding tools and just allows basic inference
from the model and also web searches if you like. This is your basic LLM
chatbot.</p>

<h3 id="personal-assistant">Personal Assistant</h3>

<p>This is where things get exciting. This is “Siri AI for jailbroken iPhones
iOS 5+.” One thing I noticed while working with AI coding tools on my Mac was
that the model was insanely good at reading arbitrary data from raw SQLite
databases. Because I happened to remember that SQLite had been the bundled and
recommended database for iPhones since the original iPhoneOS 2 SDK, I thought,</p>

<blockquote>
  <p>“What if I just found every database on the iPhone and then made it 
accessible to an model?”</p>
</blockquote>

<p>That’s how it all started. Because Strappy is a non-sandboxed app, first I made
a feature that can scan your entire home folder for SQLite databases. Then I
built some tools into the harness that allow the model to make read-only SQL
queries against the databases. Lastly, I added in a whitelisting system so that
you can control which databases the model can query.</p>

<p>With this basic harness in place, Strappy is able to make quick work of your
databases and find all sorts of information in there to answer your questions.
Yes, Strappy can read your address book, calendar, email, messages, and even
third-party apps which also generally use SQLite databases. It’s actually really
quite impressive how well the model does at parsing and organizing your data.</p>

<h4 id="data-privacy-and-safeguards">Data Privacy and Safeguards</h4>

<p>Because these databases contain personal data, I built a whitelist and only
expose bounded, read-only SQL queries. The exact safeguards are documented in
the <a href="https://github.com/jeffreybergier/Strappy-Cocoa#database-scanning">README</a>.</p>

<p>But I want to be clear: your data is being sent to someone else’s servers
without end-to-end encryption. Normal HTTPS/TLS protects it in transit between
you and OpenRouter. After that, OpenRouter chooses the cheapest provider for
that model and sends the data to that provider.</p>

<p>OpenRouter includes extensive controls to limit WHO your data is sent to and
what rules they are expected to follow via its 
“<a href="https://openrouter.ai/docs/guides/features/guardrails">Guardrails</a>” 
feature. So check that out if you are curious.</p>

<h3 id="coding-assistant">Coding Assistant</h3>

<p>Inspired by <a href="https://pi.dev">Pi.dev</a> and their simple “4 tool” solution, I 
decided to give it a try. If I implement file_read, file_write, file_edit, and
bash tools, will Strappy be able to “vibe-code” on the iPhone. And the 
answer was YES!</p>

<p>In Cydia on iOS 6 there were already basic development packages like Clang 4
and other basic tools. But I thought they were too old, so I spent some time
creating a 
<a href="https://github.com/jeffreybergier/AltivecIntelligence/tags">toolchain</a>
which includes armv7 builds of critical tools like Clang 15, ld, ldid, otool,
libtool, make, etc that work on iOS 6. The toolchain also includes some simple
scripts to manage which iPhoneOS SDK is installed on the device and to download
some prebuilt <a href="https://github.com/jeffreybergier/AltivecIntelligence/tags">libraries</a> 
I made which include essentials for retro devices like modern versions of
libcurl, cJSON, and SQLite. Once I combined this with a very basic 
<a href="https://github.com/jeffreybergier/Strappy-Cocoa/blob/main/source/shared/Resources/GuidanceSkills.json#L8">skill</a> 
for iOS development, Strappy had absolutely no issues “one-shot-ing” and
“vibe-coding” iPhone applications.</p>

<p>It’s really amazing to watch the model write, compile, install, and run apps all
locally on the iPhone, no computer needed. I say its amazing, but it really
shouldn’t be… ignoring the model part, writing and compiling apps is not a new
concept at all. It’s just something Apple restricts us from doing on iPhones.</p>

<h4 id="system-and-data-integrity">System and Data Integrity</h4>

<p>In the spirit of <a href="https://pi.dev/docs/latest/security#no-built-in-sandbox">Pi.dev</a>, 
the coding tools I made have no sandboxing or safeguards. And yes, once the
model did run
<a href="/assets/images/unenshittification/strappy/delete-apps.jpg"><code class="language-plaintext highlighter-rouge">rm -rf /var/mobile/Applications/*</code></a>
and yes, it did delete like 40 applications before I stopped it.</p>

<p>The issue is once the model has bash access, there is not really a way to
sandbox it any more aside from basic UNIX permissions. I did think about adding
a new user to the system and then running the 4 tools as that user. That would
at least protect your personal data from modification or deletion. But this is
not implemented.</p>

<p><strong>Please be careful.</strong></p>

<h2 id="how-does-strappy-work">How Does Strappy Work</h2>

<p>You may or may not know this, but the OpenAI 
“<a href="https://developers.openai.com/api/reference/responses/overview">Responses API</a>” 
is actually a very simple JSON-based web API. Even though Codex or Claude might
seem like super sophisticated applications, they are just JavaScript HTTP
clients. Their core function is to send and receive JSON data over HTTPS just
like any other application. And as I have said in 
<a href="/unenshittification/2026/02/22/Unenshittification-101.html">2026: The Year of Unenshittification</a>,</p>

<blockquote>
  <p>You don’t need an 8” supercomputer in your pocket to send, receive, and display JSON.</p>
</blockquote>

<p>Strappy is just that: a basic HTTP client that sends, receives, and displays
JSON via modern versions of libcurl, cJSON, and SQLite as provided in 
<a href="/unenshittification/2026/04/22/Altivec-Intelligence.html">Altivec Intelligence</a>.
Most of the harness is portable C wrapped in native UIKit and AppKit interfaces.</p>

<p>Strappy is free and open source, 100% AI generated, and 
<a href="https://thenewstack.io/torvalds-ai-programming-productivity/">“100% compiled”</a>. 
The <a href="https://github.com/jeffreybergier/Strappy-Cocoa#readme">README</a> contains
the architecture, tool matrix, compatibility, installation, and build
instructions.</p>

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

<p>When I first created the system prompt and the toolset that the harness would
have, I brainstormed with Codex about how the Response API worked, how tool 
inputs and outputs worked, and we got to work with what seemed like a reasonable
initial toolset. However, I quickly ran into a lot of issues that was preventing
the models from completing their task:</p>

<ol>
  <li>Skipping Critical Tools</li>
  <li>Tool calls with invalid JSON</li>
  <li>Tool calls with SQL queries that returned 0 rows of data</li>
  <li>Skipping databases that were critical for the user prompt</li>
  <li>Returned data was not useful for the model to use for inference (particularly timestamps)</li>
</ol>

<p>While none of these issues had a one-size-fits-all solution, I was able to chip
away at the problems 1-by-1. The most important part was building a simple
hill-climbing test suite. To build this suite, I chose 6 small models, a sample 
set of real databases from my iPhone 5, and a fairly complex user prompt.
Then the suite would rate the answers based on many factors:</p>

<ul>
  <li>Were the correct tools called</li>
  <li>Were the correct databases queried</li>
  <li>Was there an answer (sometimes they come back blank)</li>
  <li>Did the answer include certain key words that were required to answer the prompt</li>
  <li>How long did it take to answer</li>
  <li>How much did it cost to answer</li>
  <li>etc</li>
</ul>

<p>Using this hill-climb, I could test my fixes one at a time to improve
performance. An overview of the solutions is as follows, but the TL;DR is
<strong>Don’t let Codex or Claude write your prompts and guidance!</strong>, they will 
overwhelm smaller models in complex rules that will just confuse them.</p>

<h3 id="simplify-the-system-prompt">Simplify the System Prompt</h3>

<p>Keep the system prompt simple and direct. I
found using simple keywords like ALWAYS and NEVER really helped improve 
performance instead of being technically correct and putting a bunch of complex 
conditionals everywhere.</p>

<h3 id="simplify-tool-input-and-output">Simplify Tool Input and Output</h3>

<p>It can seem appealing to design a very
elaborate JSON schema for both the tool input and the tool output so that the 
model has total control and flexibility. However, this is a mistake. Complex
tool inputs cause the model to fail to structure the JSON properly at all.
Then complex outputs with large amounts of data quickly fill the context of 
the model and cause it to perform worse from that moment on.</p>

<p>A specific example of this was that I had the <code class="language-plaintext highlighter-rouge">database_context</code> tool
return every table and every column in the database to the model. The problem
was that some databases had 60+ columns in one table alone. This caused the 
model to simply get confused. I simplified the tool to only return the names
of the tables and the performance shot up.</p>

<h3 id="specialized-tools-can-help">Specialized Tools Can Help</h3>

<p>In general, databases contain a lot of timestamps. This was becoming a problem
because the model could not really infer accurate dates from these timestamps.
So I built <code class="language-plaintext highlighter-rouge">datetime_from_iso8601</code> and <code class="language-plaintext highlighter-rouge">datetime_to_iso8601</code> tools so the model
could convert any timestamps it wanted with precision accuracy. This got even
more complex for the model because the timestamps in SQLite are not labeled in
anyway. So the model needs to guess and then verify the original format of the
timestamp. For example, is it Apple Milliseconds, Apple Seconds, UNIX Seconds,
UNIX Milliseconds, etc. The model doesn’t always guess right, but the tool 
input and output schemas toward the model doublechecking the ISO8601 timestamp
that comes out seems plausible or not.</p>

<p>The same went for emoji. Old devices do not have very good emoji support which
leads to a lot of blank white boxes rendering. To offset this Strappy includes
FontAwesome v7 Free. However, the models are not trained on FontAwesome. So
I provide custom tools like <code class="language-plaintext highlighter-rouge">fontawesome_search</code> and <code class="language-plaintext highlighter-rouge">fontawesome_confirm</code>
so the model can find good icons to use in the answer.</p>

<p>Lastly, when it came to the coding assistant, making an iPhone app on an iPhone
is not as simple as using <code class="language-plaintext highlighter-rouge">xcodebuild</code> on your Mac. Basically, the process
involves complex makefiles, Apple SDK’s which are missing, and non-standard
ways of programming, like not using NIBs. So I created an iPhone-oriented 
toolchain that not only provided the raw tools but also provided handy bash
scripts like <code class="language-plaintext highlighter-rouge">altivec-app</code> which can create a new iPhone app from a template I 
made with all the correct settings already set up. This kind of small tooling
made the model much more efficient when asked to make a new iOS app.</p>

<h3 id="study-unknown-data-upfront">Study Unknown Data Upfront</h3>

<p>In order to increase answer quality, reduce token usage, and speed up answer
times, the harness needs to guide the model. The biggest example of this in
Strappy is <code class="language-plaintext highlighter-rouge">database_query</code>. For the model to use this tool well, it needs to
know which databases contain which kinds of data. Also, the model needs to know
how to query the data. At first, this started as a hardcoded JSON file where I
described this information and guided the model via other tools like
<code class="language-plaintext highlighter-rouge">database_list</code> and <code class="language-plaintext highlighter-rouge">database_context</code>. However, this was not scalable because I
don’t have every database for every app on my iPhone 5.</p>

<p>So I deleted the hardcoded file and built a special, fully automated mode into
the harness called “Study.” In this mode the harness groups databases by app and
then ask the model to describe the kinds of data in the database and then also
define “recipes” for accessing the data. By doing this up-front “study” step,
future runs of the Personal Assistant harness go much smoother because the
<code class="language-plaintext highlighter-rouge">database_list</code> and <code class="language-plaintext highlighter-rouge">database_context</code> tools can return more information than
just the path of the database on the disk and a list of tables in the database.
I should be clear here, this is not required, but it does help model
performance, especially with smaller models.</p>

<h3 id="audit-the-model">Audit the Model</h3>

<p>Some people have said that 
<a href="https://www.theregister.com/software/2025/05/28/sergey-brin-suggests-threatening-ai-for-better-results/1059816">“threatening the model”</a> 
improves performance. But well, I am not a very violent person. Also, I do a lot
of engineering compliance auditing at work, and so I thought “why not audit
the model?” So I actually built a full Answer Quality Check feature into the 
harness. This quality check verifies that certain rules were followed, similar
to the hill-climb suite I described above. It then replies to the model and 
asks the model to fix issues. Lastly, in the System Prompt, I clearly describe
to the model that it will be audited and for what. This trick did indeed
increase performance a lot. So much so that I made it so the quality check no
longer replied to the model, it simply showed in the user interface, then lastly
I disabled it by default. It turns out just telling the model an audit will
take place was enough to get it to follow most rules most of the time.</p>

<h3 id="the-responses-api-is-flexible">The Responses API is Flexible</h3>

<p>The Responses API is based on a concept called “items.” Each request and
response is just a list of items. Items include things like Thinking, Answer,
Tool Call, Tool Response, Prompt with a role attribute, etc. The harness
entirely controls how these items are managed and which ones are included in the
“context.” This means you can put any item into a request, even if it came from
a different model, session, or was completely made up. In fact, I found great
success doing this as part of the user’s initial prompt. With each initial
prompt, I artificially inject Tool Calls, Tool Responses, and an Agent Answer
into the initial request along with the typed in user prompt. This was BY FAR
the most effective technique for improving performance. Because it meant 
required tools were always called, and the model was reminded not only by the
system prompt bit also by “itself” on what it should do next.</p>

<p>You may have noticed in the demo videos, but the beginning of each session,
always has the first same reply from Strappy depending on the mode of the 
assistant:</p>

<ul>
  <li><strong>World Knowledge:</strong> “Strappy here, babe. Facts, line up. Let me check my 
memories and tools before I put the world in order.”</li>
  <li><strong>Personal Assistant:</strong> “Strappy here, babe. Databases, behave. Let me 
inspect my memories, skills, and approved data before I interrogate the 
evidence.”</li>
  <li><strong>Coding Assistant:</strong> “Strappy here, babe. Code, heel. Let me inspect the
environment, review my tools, and find out exactly what needs discipline.”</li>
</ul>

<h3 id="the-harness-needs-to-guide-the-user-as-well">The Harness Needs to Guide the User as Well</h3>

<p>You might think that the work of the harness is to wrangle the model to achieve
your goal. But actually, its as much of a UI issue for the human as it is for
the model. You can make the model side of the harness as complex as you want.
Your harness can constantly poke and prod the model, inject tool calls, and tool
responses, etc. But a large question becomes “what do you show to the user?” Do
you want a 100% faithful representation of what the harness is actually doing?
Or do you want to only show final answer? These are the kinds of questions, you
have to answer when designing the harness. In Strappy, I wanted to keep the
display as faithful to each request and response with all of their items as
possible and give the user full context in to what is going on, but I also
wanted it to try to fit on an iPhone screen. So to optimize the size and reduce
the information displayed by default I tried to stick to the following rules:</p>

<ul>
  <li>Each round contains a request and response</li>
  <li>Each item in each request and response has a line item</li>
  <li>Each item is expandable but collapsed by default</li>
  <li>Each round is expandable and is expanded when the prompt is in progress and then collapses after the final answer</li>
  <li>Errors show in red color and cause the row to be expanded</li>
  <li>User Prompt, “Developer” Prompt, and Answers shows in larger text with white background</li>
  <li>Answers render markdown</li>
  <li>Tools use basic javascript to render JSON as HTML</li>
  <li>Bash is a special tool which is always expanded and shows in a unique style</li>
</ul>

<p>But as the harness developer you could show so much more or so much less. Thats
why I make the claim that “The Harness Needs to Guide the User as Much as 
the Model”</p>

<h2 id="conclusion">Conclusion</h2>

<p>In my <a href="/unenshittification/2026/02/22/Unenshittification-101.html">2026 Journey to Unenshittify my Life</a>, Strappy AI was honestly a bit of a
sidetrack. I could have spent my time trying to make things I would use every
day like a YouTube app or add Instagram support to 
<a href="/unenshittification/2026/03/22/RSS-THE-PLANET.html">RSS-THE-PLANET</a>. But ever since I 
built RSS-THE-PLANET and
<a href="/unenshittification/2026/06/22/LINE-Messenger-for-Retro-Devices.html">ENIL Messenger</a>,
everyday life on my iPhone 5 has become pretty comfortable.</p>

<p>So I built the PoC for Strappy from my original SQLite database querying idea,
and since then I just couldn’t stop! It was so much fun and so different than
“regular” programming. Making a harness for an model is just so strange. But
once I got Strappy to start performing the way I wanted, it was such a great
feeling and a ton of fun! After I get ChatGPT OAuth working so I can use Strappy
with my monthly plan, I intend to use Strappy a lot. Not because what I made is
better than Codex or Claude, but because Strappy is mine and I made it for
myself and I love it.</p>]]></content><author><name></name></author><category term="Unenshittification" /><category term="AI" /><category term="OpenRouter" /><category term="SQLite" /><category term="PowerPC" /><category term="Mac-OS-X" /><category term="iOS" /><category term="Objective-C" /><category term="C" /><summary type="html"><![CDATA[Strappy AI is like Siri AI for jailbroken iPhones running iOS 5+ and Macs running Mac OS X Tiger 10.4+. Strappy queries your app's databases to answer your personal questions.]]></summary></entry><entry><title type="html">ENIL: LINE Messenger for Retro Devices</title><link href="https://jeffburg.com/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/unenshittification/2026/06/22/LINE-Messenger-for-Retro-Devices</id><content type="html" xml:base="https://jeffburg.com/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="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></feed>