The last time a new iPhone screen size landed on my desk, I noticed something was missing: the small dread I used to feel. In the past, that work always came with a quiet sigh — "time to open those files again and start adding numbers."
This time the task was actually larger, and yet I felt calm about it. When I traced back why, I landed on one unremarkable decision I'd made about six months earlier. I want to write that decision down today.
I used to brace myself a little every time a new device shipped
I've been building smartphone apps on my own since 2013. Today I run four iOS apps, including Beautiful HD Wallpapers and Ukiyo-e Wallpapers, with more than 50 million downloads between them over the years. Because they're wallpaper apps, almost everything is about filling the screen edge to edge with an image — which means every difference in resolution or safe area shows up immediately in what the user sees.
The trouble was that the code absorbing those per-device differences had quietly scattered everywhere over the years. One view branched on screen height directly. Another decided things based on whether the corners were rounded. A third compared the device model name as a raw string. Each was the quickest thing to write in the moment.
So whenever a new iPhone arrived, I'd hunt those branches down one by one and graft another condition onto each. Miss one, and a single device ends up with the wrong margin or a clipped image edge. What made the work tiring wasn't the volume — it was the unease of not knowing where the branches were hiding.
Pulling the branches into one place — a small, dull decision that paid off
The turning point came while I was updating the SDKs across all four apps at once. As long as I was in there, I decided to gather every scattered device branch into a single constants file. It wasn't a clever idea. It was just "put decisions of the same kind in the same place."
Before, code like this was sprinkled across many files:
// Branching on screen height directly, view by view
let bottomInset: CGFloat
if UIScreen.main.bounds.height >= 956 {
bottomInset = 34 // larger devices
} else if UIScreen.main.bounds.height >= 844 {
bottomInset = 28
} else {
bottomInset = 0 // devices with a home button
}After, I moved the reasoning into one home:
// DeviceMetrics.swift — only this file knows about per-device differences
enum DeviceMetrics {
/// Bottom safe-area inset. When a new screen size appears,
/// the only edit is one more line in this list.
static var bottomInset: CGFloat {
switch UIScreen.main.bounds.height {
case 956...: return 34
case 844..<956: return 28
default: return 0
}
}
}
// Call sites no longer need to care *why* a number is what it is
view.layoutMargins.bottom = DeviceMetrics.bottomInsetThe part I handed to Antigravity was the search: "find every place that reads UIScreen.main.bounds." That's exactly the kind of sweep a human misses, and the agent pulled them all out of the codebase accurately. If you want the concrete play-by-play of the new-device work itself, I've written it up in Surviving a New iPhone Resolution with Antigravity, so feel free to read that alongside this.
The agent is great at finding; deciding is still my job
This work let me draw a clean line. The agent is astonishingly good at finding where things are scattered. But deciding what shape to gather them into — the abstraction itself — still feels like territory I should keep.
Should device branches be grouped by height? By generation? By measured safe-area values? There isn't one right answer; it depends on the character of the app and where I want to take it. Antigravity can offer candidates, but which one I pick comes down to how I want to grow that app over time. I touched on this rhythm of working through bugs together with the agent in the Antigravity AI Debugging Guide — let it do the finding, keep the deciding for yourself. That split sits right with me.
Keeping devices I haven't seen yet inside the design
Once the branches lived in one constants file, something quiet shifted. A new screen size stopped being an "unknown threat" and became "one more line added to a known list."
Both of my grandfathers were miyadaiku — traditional Japanese temple carpenters. What I watched as a child was the careful squaring of measurements for a building that didn't exist yet. The act of working with their hands seemed to carry something close to faith. Only now do I understand that the work was about leaving room for what was still to come.
Maybe the same holds for software. I can't know what device ships next. But I can predict the shape of the change: "one more per-device value gets added." If that's true, then preparing a single place to absorb that change means even unseen devices are, in a sense, already accounted for in the design. I wrote about tidying things up in bulk during an SDK update in Three Things That Tripped Me Up Migrating Firebase from CocoaPods to SPM; maintainability, I'm coming to feel, is decided by this slow accumulation of small "one place" choices.
When the next screen size arrives
If you're someone who still feels that dread with every new device, start by picking the single most scattered branch and gathering just that one into a single place. You don't have to reorganize everything at once. Cleaning up even the one device branch you touch most often will change how the next device makes you feel.
I still have plenty of code I haven't tidied. Even so, adding one small place-for-change at a time feels like the dull but dependable footing that lets me keep building on my own for the long haul. Thank you for reading to the end.