Skip to content

Fix iOS 26 scene lifecycle — jFlash now runs on modern simulator#7

Open
rsharrott wants to merge 32 commits intomasterfrom
claudcode
Open

Fix iOS 26 scene lifecycle — jFlash now runs on modern simulator#7
rsharrott wants to merge 32 commits intomasterfrom
claudcode

Conversation

@rsharrott
Copy link
Copy Markdown
Contributor

Summary

  • Root cause: iOS 26 (Xcode 26.3) mandates UIApplicationSceneManifest and no longer auto-loads NSMainNibFile, so the app delegate was never created, the scene fired with nil outlets, and the app showed a black screen or crashed
  • Fix: Wire the full iOS 13+ scene lifecycle while keeping the existing NIB-based object graph intact
  • Also fixed: UIAlertView is completely banned for scene-based apps in iOS 13+; replaced all usages with UIAlertController

What changed

File Change
main.m Pass @"jFlashAppDelegate" to UIApplicationMain so UIKit creates the delegate before the scene fires
jFlash-Info.plist Add UIApplicationSceneManifest pointing at LWESceneDelegate; remove NSMainNibFile keys
jFlashAppDelegate.m Add LWESceneDelegate (separate class); load MainWindow.xib manually in didFinishLaunchingWithOptions: to wire IBOutlets; defer window.rootViewController = tabBarController until after DB+plugins open so viewDidLoad doesn't query cards before CARD_DB is attached
XIBs/MainWindow.xib Change File's Owner from UIApplicationjFlashAppDelegate; move outlet connections there; remove redundant custom jFlashAppDelegate object
HelpViewController.m Replace direct UIAlertView alloc with UIAlertController
Appirater.m Replace UIAlertView with UIAlertController
Classes/Reusable/LWE (submodule) LWEUIAlertView.m: replace all factory methods — now present UIAlertController from topmost scene window, forwarding button indices to existing UIAlertViewDelegate callers

Notes

  • The LWE submodule (Long-Weekend-Dev-Tools) has a local commit on branch ios26-compat that needs to be pushed separately to github.com:LongWeekend/Long-Weekend-Dev-Tools.git
  • Tested on iPhone 16e simulator (iOS 26 / Xcode 26.3): builds, launches, loads cards correctly

Test plan

  • Clean install on simulator (uninstall first to test first-launch DB copy path)
  • Cards load with kanji/reading/meaning on the Practice tab
  • Navigate to Study Sets, Settings, Help tabs — no crashes
  • Tap Help → Support button — alert presents correctly (UIAlertController)
  • Subsequent launches use cached DB (no copy) and load correctly

🤖 Generated with Claude Code

rsharrott and others added 30 commits April 22, 2026 11:41
iOS 26 (Xcode 26.3) introduces mandatory UIApplicationSceneManifest and no
longer auto-loads NSMainNibFile, breaking the original NIB-based launch flow.

Changes:
- main.m: pass @"jFlashAppDelegate" to UIApplicationMain so UIKit creates
  the delegate before the scene fires (was nil in scene callback)
- jFlash-Info.plist: add UIApplicationSceneManifest pointing at LWESceneDelegate;
  remove NSMainNibFile keys to avoid double-load conflict
- jFlashAppDelegate.m: add LWESceneDelegate (UIWindowSceneDelegate) that creates
  a scene-backed UIWindow; load MainWindow.xib manually in
  didFinishLaunchingWithOptions: so IBOutlets are wired before any outlet-based
  code; defer window.rootViewController = tabBarController to
  _openUserDatabaseWithPlugins (after DB+plugins are ready) so viewDidLoad
  methods don't query cards before CARD_DB is attached
- XIBs/MainWindow.xib: change File's Owner from UIApplication to
  jFlashAppDelegate and move all outlet connections there; remove the now-
  redundant custom jFlashAppDelegate object; fix tabBarController delegate outlet
- HelpViewController.m: replace direct UIAlertView with UIAlertController
- Appirater.m: replace UIAlertView with UIAlertController
- Classes/Reusable/LWE: update submodule to commit that replaces LWEUIAlertView
  factory (UIAlertView) with UIAlertController presentation

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
iOS 15+ introduces a transparent scrollEdgeAppearance by default, which causes
the window background to bleed through into the home-indicator safe area below
the tab bar. Explicitly configure UITabBarAppearance with the default opaque
background so the tab bar fills the safe area consistently.

Also set window.backgroundColor = black so the splash/loading phase
doesn't flash white on dark-theme devices.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Two-part fix:

1. Set tabBar.translucent = NO and use configureWithOpaqueBackground
   so UIKit sizes child view controllers' views to end above the tab bar
   rather than extending underneath the new iOS 26 floating-glass tab bar.
   Without this, the glass/white region of the floating tab bar was visible
   between the action-bar buttons and the tab bar items.

2. Set StudyViewController.view.backgroundColor to match the action bar's
   gray (#E6E6E6) so that any residual gap shows a consistent color rather
   than the near-white pinkish default from the XIB.

Also removed the temporary diagnostic NSLog statements added during debugging.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Add UILaunchScreen to Info.plist so iOS exits 320x568 compatibility
mode and renders at the device's native resolution. Update autoresizing
masks on root views and key subviews across all main XIBs (StudyView,
ActionBarViewController, CardViewController, ProgressBarViewController,
ExampleSentencesView, ProgressView, SearchView, AddStudySetView,
UserDetailsView) to widthSizable so layouts fill the full screen width
on current iPhones.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Push the top progress bar below the notch by repositioning it to
safeAreaInsets.top in viewSafeAreaInsetsDidChange instead of a
hardcoded y=20 that predates Face ID devices.

Evenly distribute action bar buttons across the full screen width
in viewDidLayoutSubviews rather than relying on autoresizing-mask
proportional layout, which broke when the screen widened from 320pt
to 390pt.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Tab bar: isa-swap UITabBar to LWECenteredTabBar via object_setClass so
  items center vertically in the full bar height; XIB customClass is
  silently ignored by UIKit for tabBar properties, making this the only
  reliable approach
- Card layout: add CardViewController.layoutCardSubviews to position
  reading/headword at ~25% of card height (~30% from screen top) and
  extend the meaning webview to fill the remaining space down to the
  mood icon
- StudyViewController: fill card VC view to full container height
  (no vertical centering), call layoutCardSubviews on each layout pass,
  and push the reveal button below the headword area so the reading
  toggle is tappable without triggering the definition reveal
- Definition HTML: replace fixed-width table layout with flexbox so
  text centers correctly on all screen widths
- Study view labels: left-align card set name, right-align card count,
  both with 8pt symmetric edge padding

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Replace deprecated SenTestingKit with XCTest in all test files
- Update libz and Twitter framework references in project file
- Update submodule pointers and XIB metadata after Xcode resave
- Update sqlite3 subproject build settings

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Replace old PNG images (practice-btn-*.png) with SF Symbol icons:
plus.circle.fill (blue/actions), checkmark.circle.fill (green/right),
xmark.circle.fill (red/wrong), archivebox.circle.fill (orange/bury it).

Root cause of prior failure: actionBarDidChangeMode: was calling
loadNibNamed:owner: which re-routed all IBOutlets to orphaned buttons
outside the view hierarchy, so hide/show calls and style changes
were applied to buttons the user never saw. Removed these redundant
NIB reloads — _setupSubviews already creates fresh instances.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
UIActionSheet was silently ignored on modern iOS. Replaced with
UIAlertController (action sheet style) with inline action handlers,
removing the now-unnecessary UIActionSheetDelegate conformance and
SVC_ACTION_* button index constants.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
FTS_DB (30MB) and EX_DB (52MB) are now included in the app bundle so
users get them immediately without a separate download. New installs
install all three bundle plugins on first launch; existing 1.8 users
pick them up via a 1.8→1.9 migration that registers the bundle plugins
directly in NSUserDefaults before the DB is opened.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Replaced hardcoded 315px/265px pixel widths (original iPhone era) with
fluid 100% widths and 14px body padding, and added a viewport meta tag
so the web view renders at device width rather than a fixed 320pt canvas.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Zeroed XIB left margin and removed body padding so content fills the
full viewport width. List number indentation (padding-left on ol) is
now the only visual offset from the edge.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Replace UIWebViewDelegate with WKNavigationDelegate
- Create WKWebView programmatically in viewDidLoad (WKWebView cannot
  be an IBOutlet in legacy XIBs); remove UIWebView from the XIB
- Replace shouldStartLoadWithRequest: with
  decidePolicyForNavigationAction:decisionHandler:
- Replace synchronous stringByEvaluatingJavaScriptFromString: with
  evaluateJavaScript:completionHandler:
- Remove UIWebView+LWENoBounces category; use scrollView.bounces = NO
- Link WebKit.framework in jFlash target

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Push scrollView below progress bar in viewSafeAreaInsetsDidChange so
  sentences start below the progress bar instead of behind it
- Set example sentences container to full scroll-page width instead of
  centering a 320pt-wide XIB frame, removing the phantom side margins
- Add 10px horizontal body padding in HTML so text and Read button have
  matching visual insets from both edges
- Fix _pluginDidInstall: version check (was == "1.1", should be != "1.1")
- Remove _updateSettingsFrom18to19 migration (bundled plugins are handled
  entirely by the first-load install path)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Removed the hardcoded provisioning profile UUID
84DB2FA3-1C5B-44E3-961A-B7F863341332 from Debug, Release, and Beta
build configs. All three already had CODE_SIGN_STYLE = Automatic and
DEVELOPMENT_TEAM set, so Xcode will now manage the profile automatically.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Add [self.view setNeedsLayout] in _setupSubviews so viewDidLayoutSubviews
  fires after every study set change, resizing the new cardViewController.view
  (loaded from a 320x278 NIB) to fill its container correctly
- Clean up card HTML templates: remove legacy -webkit-flex centering,
  use simpler padding-top approach that works with our explicit frame layout
- Switch Info.plist bundle ID to $(PRODUCT_BUNDLE_IDENTIFIER) and remove
  obsolete orientation/icon/statusbar keys

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This app was designed for light mode only. On dark mode iPhones,
systemBackgroundColor (used in ActionBarViewController) resolved to
pure black, making the action bar invisible. Forcing light mode on
the window fixes all system color adaptations app-wide without
needing to patch individual color references.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- CardTagTest.m: 15 new tests covering card retrieval, hydration, tag
  membership, and UserHistoryPeer level transitions (correct/wrong/bury)
- TagTest.m: 6 new tests for tag properties, editability, CRUD, and
  retrieval by name and ID
- PluginTest.m: 7 new tests for plugin type detection (database/directory)
  and version comparison
- JFlashSearchTest.m: 5 new tests for kana/English/nonsense search and
  keywordIsReading/keywordIsHeadword classification
- JFlashPluginMigrationTest.m: 2 new tests for unknown-key resilience and
  cross-format version comparison

Also fixes jFlashTest build: adds missing source files to the test target
(UserHistoryPeer, UserPeer, User, DisplaySearchedSentenceViewController,
JREngage) and removes the legacy RunUnitTests script phase that referenced
a tool removed in modern Xcode.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Tapping the speaker icon next to the headword reads the word aloud
using AVSpeechSynthesizer with the best available ja-JP voice:
premium (iOS 16+) → enhanced → standard fallback.

The button is positioned to the right of the headword row in
layoutCardSubviews and is visible before card reveal, matching
the pattern of the reading-toggle button. Wrapped in LWE_JFLASH
so the cFlash audio-plugin path is unaffected.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Splits "kana - romaji" reading label text and speaks only the kana
portion, giving the TTS engine the exact pronunciation rather than
relying on it to interpret kanji.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Follows the existing fake-URL interception pattern used by the Read
and Add buttons. Each sentence gets an inline 🔊 button whose href
encodes the sentence ID; tapping it looks up the stored sentenceJa
text and speaks it via AVSpeechSynthesizer with the best available
ja-JP voice (premium → enhanced → standard).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Instead of narrowing the headword container to reserve fixed space on
the right, the scroll container remains full-width so center alignment
is unaffected. _updateSpeakBtnPosition computes the visual right edge
of the rendered text (containerCenter + textWidth/2) and places the
36pt button 6pt to its right. Called from both layoutCardSubviews
(on layout/set-change) and _prepareView: (on every card change) so
position tracks the current word's actual rendered width.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
The 🔊 emoji was written to the source file as a corrupt 3-byte
sequence (0xF0 lead byte dropped). Replace with the HTML entity
&#x1F50A; which is pure ASCII in the source and decodes correctly
in WKWebView.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Replaced corrupted emoji (&#x1F50A;) with inline SVG speaker icon (pure
ASCII, reliable WebKit rendering). Moved button inside showWordsDiv so
it appears to the right of the Read button. Handles both plugin v1.1
(standalone float-right) and v1.2 (alongside Read button) cases.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Grow SF Symbol icon from 34pt to 44pt to better fill the bar height
- Add imageEdgeInsets to shift icon up, leaving clear space for labels
- distributeButtonsEvenly: each button now fills an equal share of the
  full bar width (floor(width/n)) rather than using fixed widths + gaps
- Apply modern SF Symbol styling to prevCardBtn/nextCardBtn in browse
  mode (chevron.left/right.circle.fill, gray tint with "prev"/"next"
  labels), replacing the legacy practice-btn-last/next PNG images
- viewDidLayoutSubviews: include prev/next in label-positioning loop

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Add vertical-align:middle to the inline SVG so the speaker icon sits
at the same midline as the Read button text instead of dropping to baseline.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Drop APP_TWITTER and APP_FACEBOOK constants and their extern declarations
- Remove social section from JapaneseSettingsDataSource and
  ChineseSettingsDataSource return arrays (both update-available paths)
- Remove cell rendering and tap-handling branches for Twitter/Facebook
- Remove UIWebView delegate methods that were only used to load the
  Twitter/Facebook URLs
- Drop UIWebViewDelegate conformance from SettingsViewController

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Drop the kLWEBackupSection enum value and reduce NUM_SECTIONS in both
  LWE_JUNIOR and non-JUNIOR paths
- Remove all backup/restore UI: section header, cell rendering, row
  count, tap handling (Backup Now / Restore Now / Logout rows)
- Remove backup and restore methods, and all LWEBackupManagerDelegate
  callbacks (statusDidChange, currentProgress, didBackup, didRestore,
  didFail×2)
- Remove kBackupConfirmationAlertTag / kRestoreConfirmationAlertTag and
  their alertView:clickedButtonAtIndex: branches
- Drop BackupManager property from StudySetViewController header/impl
- Remove BackupManager.h/m and LWEJanrainLoginManager.h/m from the
  project (pbxproj PBXFileReference, PBXGroup, and all Sources build
  phases) and delete the files

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
… screens

- Remove Flurry library (FlurryLib dir, pbxproj entries, LWEAnalytics dead code)
- Remove Backup Custom Sets and Feedback from Help menu
- Fix Change Difficulty segmented control overlapping nav bar: move into tableHeaderView
- Fix Get Updates button/label overlapping nav bar: move into tableHeaderView
- Both settings screens now span full screen width with flexible autoresizing

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Remove APP_TEXT_SIZE setting and its Normal/Large/Huge constants; map
UIContentSizeCategoryXxx to CSS px and scale factors at runtime instead.
Subscribe to UIContentSizeCategoryDidChangeNotification to reload cards
when the user changes system text size. Fix hardcoded line-height values
in WKWebView HTML templates (21px/32px → 1.4) so meaning text never
overlaps at accessibility sizes. Apply UITableViewAutomaticDimension
across Settings, StudySet, Help, Plugin, and Search screens so list
cells grow cleanly with large Dynamic Type sizes.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
rsharrott and others added 2 commits April 25, 2026 10:18
Remove Twitter integration references and Follow us on Twitter links.
Fix typo "please us know" → "please let us know" in Corrections.
Replace "tweet the word" with "share the card" in Practice and Browse.
Fix CFlash Integration page saying "launch jflash" → "launch cflash".
Remove outdated screenshots whose UI changed (card buttons, browse
buttons, difficulty screen).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- CardTagTest: add UserHistoryPeer level transitions 2→3, 3→4, wrong from 0, wrong from 5
- TagTest: add deleteTag and retrieveTagListByGroupId coverage
- CardModelTest (new): meaningWithoutMarkup HTML stripping and headwordIgnoringMode: logic
- JapaneseCardTest (new): JapaneseCard.reading for kana, romaji, and both modes

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
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