~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain-ios

1 collaborator
0
1

Builds older than the server's minimum ask to be updated

Merged
Merged chadtmiller.com asked to merge into main from grain.social/grain-ios:update-gate

What changes

The gate. AppUpdateGate fetches AuthManager.serverURL + /app-version.json at launch and each time the app becomes active (5 s timeout, overlapping checks skipped), and compares ios.minimumBuild with the running CFBundleVersion. Below it, UpdateRequiredView replaces the whole view tree: "Update Grain", "This version of Grain is no longer supported. Update to keep using it.", and an Update button opening storeUrl (the App Store listing if missing or not https/itms-apps). It is styled after the login screen.

Offline and failures. The last minimum read is kept in UserDefaults and read at startup, so a blocked build opened offline is blocked at once. A failed request, a non-200, or malformed JSON leaves the cached decision alone; with nothing cached it never blocks. A missing ios key or minimumBuild means no minimum and clears the cache, which is how lowering the minimum unblocks a build.

Why

grain's server is moving off hatk, and the builds installed before the new API will stop working when it does. Today the app has no way to tell anyone to update. This ships first, on the current server, so at cutover the minimum is raised and older builds show this screen instead of errors. The server half is grain.social/grain's "Serve minimum app versions at /app-version.json".

How it was checked

18 XCTests in GrainTests/AppUpdateGateTests.swift: build vs minimum, missing fields, malformed JSON, a wrong-typed minimumBuild, the cache surviving a failed fetch, a lowered minimum lifting the block. Run once on the iPhone 17 Pro Max simulator: 18 passed. swiftformat and swiftlint clean on the four files. The full suite was not run, and the screen was not looked at in the running app.

Shortcomings

  • CFBundleVersion is not the 59 in project.yml: Xcode Cloud replaces it with its own run counter when archiving, and DEVELOPMENT.md says build numbers need only increase within a marketing version. Before raising minimumBuild, check in App Store Connect that build numbers keep climbing across versions; if a new version could start lower, the gate must compare the marketing version too.
  • The story comment sheet lives in a window of its own. It should go with the story viewer when the tree is replaced, but that was not checked on a device.

🤖 Generated with Claude Code

chadtmiller.com added 1 commit
Approved

This adds a launch and foreground check of /app-version.json. If CFBundleVersion is below ios.minimumBuild, UpdateRequiredView replaces the app tree. The last minimum is cached so a blocked build stays blocked offline.

I found nothing that must change. The gate fails open when there is no cache and a failed or malformed response leaves the cached decision alone. A lowered or missing minimum clears the cache, and the store URL is limited to https or itms-apps. The tests cover each of these and use a namespaced UserDefaults through GrainTestCase, so they don't leak state.

Two things for you to do before the minimum is raised. Both are in your Shortcomings list:

  • Confirm in App Store Connect that Xcode Cloud build numbers keep climbing across marketing versions. If they don't, the gate can block current builds.
  • Look at the screen on a device. It hasn't been run in the app, and the story comment sheet's separate window hasn't been checked.
chadtmiller.com added 1 commit
Approved

Carried forward: the change this pull makes at c5aace157361cdb74ab76243792db3b2865a80ae is the one reviewed at a90e88ce7193c9fbe19fe02f65945473bbcde92f, so that review stands. What is new comes from its base.

Merged as 0d216b6