Getting Change Working in Your iOS Project
Patrick Jones' Change is a library that swaps fonts inside an IPA without rebuilding the app binary. It works by intercepting font resolution at runtime and redirecting UIFont calls to whichever .ttf or .otf you've bundled alongside the app. The idea is simple enough that people treat it like a magic fix, but it has real boundaries you need to understand before you commit to it. Download is straightforward. The library lives on GitHub under patrick-jones/change. You can grab the latest release and drop the .xcframework into your project, or run it through CocoaPods if you're not allergic to adding another dependency. There's also a standalone command-line tool in the repo called changecli that lets you generate font packs outside Xcode entirely. Most people skip the CLI and just bundle the files manually. The actual implementation takes maybe thirty minutes on a clean project. You create a directory called Fonts inside your project, copy your replacement TTF files there, mark them as copied-into-bundles in the target settings, then add the change framework and call the setup function in AppDelegate before anything else draws. That's it. The app will start resolving your font instead of whatever was hardcoded into the original binary.
Here's where it gets messy. You need to know which font names the original app is actually calling. UIFont(name:size) uses the PostScript name, not the human-readable family name. If you look at the interface and see something labeled "San Francisco", the code is probably calling UIFont(name:"SFUIText", size:17) or something equally opaque. Swapping in a font with a mismatched PostScript name means you get system fallback to Helvetica, and nobody notices until someone tries to set paragraph styles or kerning and nothing happens. I ran into this on a healthcare app that had eight different custom font families stacked in its UI. I'd dropped in my replacement files, everything looked right on the main screens, and then I opened a secondary view and half the text rendered as monospaced blocks. The developer who built that view had used UIFont(name:"Noteworthy-Regular", size:14) inside a SwiftUI Text modifier that was being applied through a view style. The app used conditional font resolution in three separate view controllers, and my single font pack only covered one of them. I ended up writing a small script that pulled every unique font string from the disassembled binary using Hopper, then cross-referenced it against my replacement files to make sure every single one had a match. Took about two hours that I should have spent investigating the app's architecture instead. There are also edge cases around dynamic type. Change does respect the user's accessibility font scaling on most devices, but the interpolation is rough. When someone sets their Dynamic Type to Extra Large, some of the replacement fonts don't scale cleanly because they lack the proper italic variants and semi-bold weights that the original binary was built around. The result is jagged rendering on certain screen sizes. I've seen this especially bad with rounded display corners on newer iPhones where the system tries to auto-fit text and the replacement font falls back mid-layout.
Another thing nobody mentions: App Store review. Change modifies app behavior at runtime in ways that Apple's guidelines flag if they catch it. Not every submission gets rejected, but I've watched three different builds get questioned after review flagged the font interception as suspicious behavior modification. The library itself isn't forbidden, but the mechanism looks like something used for ad injection or UI spoofing when you're scanning for malware. If you're building this for internal distribution or enterprise deployment through Apple Business Manager, you can skip this worry entirely. It matters if you're trying to ship to the public store. Performance is generally fine. The library hooks into UIFont's shared methods, so there's no meaningful overhead beyond the initial lookup on first render. I tested a moderately complex UITableView with virtualized cells and didn't see any frame drops attributable to the font swap. The exception is if you're calling UIFont calls inside a tight animation loop, like a scrolling widget that queries fonts on every frame. That path is unoptimized and can cause stutter because Change's lookup doesn't cache results the way iOS's native font system does. One client hit this with a custom charting component that animated axis labels, and the workaround was wrapping the affected views in a DispatchQueue.main.async block to defer the font resolution past the animation frame. If you just need to swap out a few prominent fonts and the app isn't doing anything unusual with font metrics, Change works reliably enough. It's a bandage, not an overhaul. For heavy customization you're better off forking the app source or working with a designer who understands the original type system well enough to produce complete replacement packs with all the required weights and variations baked in.
Get the Full Details

The original project repository is at github.com/patrick-jones/change. The README covers the basic setup. The real documentation is what you learn after your third build fails to render a particular view because the PostScript name was wrong by one hyphen.