ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-06-15Advanced

Stop Adding a Ternary Every Time a New iPhone Ships: A Table-Driven Resolution Design

Every time a new resolution arrived—iPhone Air, 17 Pro—I was bolting another screen-size ternary onto a 29-branch pile. Here is how I reshaped that into a table-driven design where adding the next device is a one-line data change, with the actual Swift from my wallpaper apps.

ios28swift7app-dev31antigravity347refactoring6

Premium Article

As an indie developer maintaining four wallpaper apps in parallel, the chore I most wanted gone was the per-device resolution work that lands with every new iPhone. When the iPhone Air (420×912 pt) and 17 Pro (402×874 pt) appeared, the if and ternary checks that select a background image by screen size were scattered across 29 spots inside a single DefineManager file. Fixing one meant hunting through the other 28 for the spot I had forgotten.

This article is the record of replacing that "branches multiply with every device" structure with a small table-driven design. The code is written so you can lift it straight into your own app.

When branches scatter, new models bite back

The fastest way to show the problem is the original code itself. Back then the check looked like this.

// Before: the same height check lived in 29 places across DefineManager
let bgName: String
let h = UIScreen.main.bounds.height
if h == 956 {            // Pro Max
    bgName = "bg@3x-max"
} else if h == 912 {     // iPhone Air <- add a line here for each new model
    bgName = "bg@3x-air"
} else if h == 874 {     // 17 Pro <- and here
    bgName = "bg@3x-pro"
} else if h == 852 {     // 15 / 16 standard
    bgName = "bg@3x-standard"
} else {
    bgName = "bg@2x-classic"
}

The pain is not that the logic is wrong—it is correct. The pain is that the same decision is duplicated, so the "unit of change" no longer matches the shape of the code. One decision (support a new device) explodes into 29 code edits. Miss one during the rewrite and that single device shows a blurry or stretched asset. I once shipped exactly that gap and got crash reports where one model loaded the wrong asset.

There is a second trap hiding in the strict == match. You cannot know the logical resolution of Apple's next device in advance. An unknown device falls into the else branch and grabs the lowest-resolution bg@2x-classic—so the newest, largest screens get the worst image. That is the exact behavior you least want.

Think in "which profile," not "which device"

The pivot was changing the subject of the decision. The old code asked, "Is this device a Pro Max?"—the device is the subject. Replace that with, "Which delivery profile does this screen size belong to?"

What the app actually needs is never the marketing name of the device. It needs only the operational attributes: which asset suffix to load, and what safe area to assume when laying out. So gather those attributes into one value object, put them in a table, and make that table the single source of truth.

With that shift, the number of branches stops depending on the number of devices. There is exactly one decision in code, and adding a device becomes "one more row in the table."

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Move from rewriting screen-size checks in dozens of places on every new device to adding support with a single line of data
Learn the concrete Swift implementation—profile table, resolver, launch-time cache—built around device profiles instead of device names, ready to copy into your app
Apply a resolver that safely snaps unknown future devices to the nearest profile, plus an XCTest that verifies every simulator size, to your own App Store app today
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

App Dev2026-05-20
Two Weeks of Letting Antigravity Translate Localizable.xcstrings Across 8 Languages
A two-week log of letting Antigravity draft 8-language translations for new keys added to Localizable.xcstrings. What I delegated, what I kept under my own judgment, the unexpected behaviors, and how I reconciled drafts against the existing translation corpus.
App Dev2026-05-14
I Let Antigravity Take Over My 10-Year-Old Wallpaper App — What Happened Next Surprised Me
A real case study of handing a wallpaper app's Swift codebase — built since 2014 — to Antigravity's Background Agent. What it got right, what it missed, and what that taught me about AI-assisted refactoring.
App Dev2026-05-13
Implementing Google ML Kit with Antigravity: What the Docs Don't Tell You
A practical guide to integrating Google ML Kit into iOS and Android apps with Antigravity. Covers text recognition, face detection, the Xcode 15 SPM bug workaround, and honest notes on where AI assistance helps — and doesn't.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →