Skip to content

MOB-97: port UIScene lifecycle adoption (Xcode 27 requirement) - #34

Merged
GenericJam merged 2 commits into
masterfrom
fix/mob-97-uiscene-lifecycle
Aug 25, 2026
Merged

MOB-97: port UIScene lifecycle adoption (Xcode 27 requirement)#34
GenericJam merged 2 commits into
masterfrom
fix/mob-97-uiscene-lifecycle

Conversation

@GenericJam

@GenericJam GenericJam commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Summary

Xcode 27 requires scene-based app startup; the current template boots everything in AppDelegate.application:didFinishLaunchingWithOptions:, which no longer works. Ports the fix already verified externally in the downstream app (commits 13dc2ab + 75ff1aa):

  • AppDelegate: drops the window property, didFinishLaunchingWithOptions: reduced to a bare return YES;. Every other method (orientation mask, push-token handlers) untouched.
  • New SceneDelegate (same file): scene:willConnectToSession:options: creates the window via initWithWindowScene: and sets rootViewController/makeKeyAndVisible on every call (a scene can disconnect/reconnect without relaunching the process) — but wraps mob_register_plugins()/mob_init_ui()/the BEAM-boot pthread in a static dispatch_once_t, since a second erl_start in the same process is fatal and nothing in mob's native layer guards against it.
  • Info.plist: UISceneConfigurations added alongside the existing UIApplicationSupportsMultipleScenes=false.

Test plan

  • mix test — 319 passed (the existing AppDelegate.m content assertions are substring checks, order-independent of which class contains the code — no test changes needed)
  • mix test --only lint
  • Built a dev archive from this branch, generated a fresh app, and ran clang -fsyntax-only against the real rendered AppDelegate.m on iOS Simulator SDK 26.5 (Xcode 27 itself isn't available on this machine) — zero warnings/errors, and confirms the old initWithFrame:/UIScreen.mainScreen deprecation warnings are gone
  • Actual Xcode 27 build — can't verify, not installed here

Linear: MOB-97

🤖 Generated with Claude Code

GenericJam and others added 2 commits August 25, 2026 01:22
Xcode 27 requires scene-based app startup; booting everything in
AppDelegate.application:didFinishLaunchingWithOptions: no longer works.
Ported the fix already verified externally in the `clarity` app
(commits 13dc2ab + 75ff1aa):

- AppDelegate: drop the `window` property, strip
  didFinishLaunchingWithOptions: to a bare `return YES;`. Every other
  AppDelegate method (orientation mask, push-token handlers) is
  untouched.
- New SceneDelegate (same file): scene:willConnectToSession:options:
  creates the window via initWithWindowScene: and sets
  rootViewController/makeKeyAndVisible on every call (a scene can
  disconnect/reconnect without relaunching the process), but wraps
  mob_register_plugins()/mob_init_ui()/the BEAM-boot pthread in a
  `static dispatch_once_t` so the BEAM only ever boots once per
  process -- a second erl_start in the same process is fatal and
  mob's native layer has no guard against it.
- Info.plist: UISceneConfigurations added alongside the existing
  UIApplicationSupportsMultipleScenes=false.

Verified: mix test (319 passed, incl. the existing AppDelegate content
assertions -- they're substring checks, order-independent of which
class contains the code, so no test changes needed), mix test --only
lint, and a real clang -fsyntax-only pass (iOS Simulator SDK 26.5, not
27 -- unavailable on this machine) against a freshly `mix mob.new`-
generated app built from a dev archive of this worktree: zero
warnings/errors, and critically no more of the initWithFrame:/
UIScreen.mainScreen deprecation warnings the old code had.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two gaps flagged in review, both addressed:

1. Generator tests asserting the rendered AppDelegate.m declares
   UIWindowSceneDelegate and calls dispatch_once(&<token>, ...) at the
   actual call site -- not just the dispatch_once_t type declaration,
   which a careless "simplification" could leave behind after removing
   the real guard (verified: temporarily stripped the call in the
   template, confirmed the test fails, restored it). Mirrors the house
   pattern set by the 16 KB fix's generator test. Also asserts
   Info.plist's UISceneDelegateClassName.

2. decisions/2026-08-25-uiscene-lifecycle-xcode27.md, explicitly
   carrying the "existing apps need a hand-port" caveat (same pattern
   as 2026-06-17-android-16kb-page-size.md) and pointing at MOB-97 /
   the clarity app commits for anyone porting a different existing app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@GenericJam

GenericJam commented Aug 25, 2026

Copy link
Copy Markdown
Owner Author

Addressed both gaps from review:

  1. Generator test pinning the invariantsproject_generator_test.exs now asserts the rendered AppDelegate.m declares UIWindowSceneDelegate and calls dispatch_once(&<token>, ...) at the actual call site (deliberately not just the bare "dispatch_once" substring, which also matches the unrelated dispatch_once_t type declaration and would miss someone removing just the call). Verified this actually catches the regression: temporarily stripped the dispatch_once(...) call from the template, confirmed the test fails with the expected message, restored it, confirmed green again. Also asserts Info.plist's UISceneDelegateClassName.

  2. Existing-apps caveatdecisions/2026-08-25-uiscene-lifecycle-xcode27.md, following the same pattern as 2026-06-17-android-16kb-page-size.md: explicitly states AppDelegate.m/Info.plist are app-owned and copied at generation, so existing apps need a hand-port (not picked up by a dep bump), and points at MOB-97 + the downstream app's commits for anyone doing that port.

Pushed as a new commit on this branch.

@GenericJam
GenericJam merged commit 34ddd51 into master Aug 25, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant