Modernize for iOS 26: SPM migration, Liquid Glass toolbar, settings polish#13
Open
leathalman wants to merge 6 commits into
Open
Modernize for iOS 26: SPM migration, Liquid Glass toolbar, settings polish#13leathalman wants to merge 6 commits into
leathalman wants to merge 6 commits into
Conversation
Foundational refresh to get the project building cleanly on Xcode 26 /
iOS 26 and to match modern iOS conventions:
- Migrate CocoaPods → SPM. Drop Firebase/DynamicLinks (shut down 2025-08);
store referral URLs directly. Remove Podfile, Podfile.lock, Pods/, and
Jotify.xcworkspace.
- Raise deployment target 13 → 15. Enables UIButton.Configuration and
per-VC navigation appearance APIs, and clears six dead iOS 14
availability guards.
- Fix multiple iOS 26 rendering issues:
- Nav bar color wasn't applied in detail views because iOS 26 prefers
per-VC navigationItem appearance; configureNavigationBar moved from
UINavigationController onto UIViewController.
- `EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64` was hiding iPhone
simulators on Apple Silicon.
- UIToolbar input accessory view produced SwiftUI-bridge constraint
spam on iOS 26; replaced with a new KeyboardAccessoryView that hosts
a SwiftUI `.buttonStyle(.glass)` bar via UIHostingController on iOS
26+ and falls back to the existing UIKit stack on older iOS.
- Bar is now pinned to view.keyboardLayoutGuide.topAnchor so it tracks
the keyboard with matching animation.
- Settings cells use UIListContentConfiguration and the system
disclosure indicator instead of a custom chevron.circle.fill image;
switch cell uses `accessoryView = UISwitch()` instead of a manual
70pt trailing offset.
- Search bar: unified content/date filter, UIMenu-driven scope picker,
stacked placement forced so the iOS 26 bottom-integrated search
doesn't end up behind the keyboard.
- Filled-style buttons on onboarding, paywall, and privacy-unlock VCs
via UIButton.Configuration.filled() with cornerStyle = .large.
- Dynamic type: text in NoteCell, DetailOnboardingController, and
BuyPremiumController scales with UIFontMetrics / preferredFont.
- Remove defensive hacks that iOS 26 now handles correctly: force-
unwrap-crash in UIViewController.rootViewController (falls back to
self), hide/show nav-bar redraw trick in EditingController, stale
title-color hacks in NoteCollectionController and SettingsController.
- SignInWithApple{Black,White} were `final class : UIViewRepresentable`
which newer SwiftUI runtimes assert on; now structs.
- Replace deprecated APIs: UIApplication.shared.windows → firstKeyWindow
helper; UNNotificationPresentationOptions.alert → .banner/.list/.sound;
Firebase `credential(withProviderID:…)` → `credential(providerID:…)`;
`updateEmail(to:)` → `sendEmailVerification(beforeUpdatingEmail:)`.
Also: strip CocoaPods BoringSSL-GRPC `-GCC_WARN_INHIBIT_ALL_WARNINGS`
flag (clang 17 rejects it) and add a .gitignore.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Drop the UIMenu-based Content/Date scope filter and the nav bar button that hosted it. Search now always matches against both note body and displayed date — simpler, fewer controls, nothing to configure. - Remove the custom `transition = Transition(style: .push, duration: 0.15)` in PageBoyController. In Pageboy 4+ a non-nil transition replaces UIPageViewController's gesture-tracked animator, which made swipes feel laggy after the dependency bump. iOS 26's keyboard animation is fast enough that the artificial slow-down (originally added to let the keyboard catch up when programmatically scrolling to the write-note page) is no longer needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`cellForItemAt` was running per-dequeue work that added up quickly: - A new UILongPressGestureRecognizer was attached to every cell's contentView on every reuse. Reused cells accumulated recognizers (one per scroll-by), and every touch had to consult all of them. Replaced with a single long-press recognizer on the collection view — the existing handler already uses `sender.location(in: collectionView)` to resolve the index path. - `note?.color.getColor()` was called five times per cell, and `.isDarkColor` recomputed luminance each time. Cache both once. - `shouldRasterize = true` was set on both `cell.layer` and `cell.contentView.layer`. For solid-color rounded rectangles the rasterization cost outweighs the draw cost, and toggling it on every dequeue invalidates the cache anyway. - Corner radius was re-applied on every dequeue. Moved to the cell's init so it's set once. No behavior change, just fewer allocations per frame. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The search controller was being installed on `viewWillAppear` and torn out on `viewDidDisappear`. On iOS 26's stacked search bar layout, this forces a nav bar rebuild on every transition away from the Notes list — hence the consistent stagger whenever the user tapped the gear, the + button, or a note card. Transitions inside Settings (push/pop between plain UITableViewControllers with no search bar) didn't have this overhead, which is why they felt snappy by comparison. Also remove the ViewAnimator dependency and its `animateVisibleCells()` call. It was running in `viewDidLoad` before Firebase had delivered any notes, so it was animating zero cells on the initial load anyway. - Move setupSearchBar() and setupNavigationBar() from viewWillAppear to viewDidLoad so they run once. - Drop the `navigationItem.searchController = nil` teardown on viewDidDisappear. - Remove ViewAnimator import + the `animateVisibleCells` helper; unregister the SPM package from the target. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ead notifications Cleanup pass on patterns left over from older Swift/iOS conventions: - `ColorManager.noteColor = UIColor()` and `bgColor = UIColor()` used the placeholder-UIColor default, which crashes on `isEqual:` because it has no color space. Replaced with `.systemBlue` / `.systemBackground` — both are always overwritten before observation, the defaults just prevent latent crashes during early launch. - `arc4random_uniform(UInt32(count))` → `Int.random(in: 0..<count)`. - Replace the manual `Timer.scheduledTimer` idle auto-save in both EditingController and WriteNoteController with a Combine debounce. WriteNoteController resets its subscription inside `handleSend` so a pending debounced value from the just-sent note can't fire against the fresh blank note that follows. - `"disableSwipe"` / `"enableSwipe"` NotificationCenter posts were posted but never observed anywhere in the codebase — pure dead code. Removed. - `"updatePureDarkMode"` NotificationCenter pattern replaced with natural `viewWillAppear` refresh of `view.backgroundColor`. The user toggles Pure Dark Mode in CustomizationSettingsController and pops back; each ancestor VC picks up the new `ColorManager.bgColor` on appearance without needing a broadcast. - Magic `tag = 007` on the GradientAnimator subview replaced with a type-based `UIView.gradientAnimator` lookup. Both set/remove and the iPad rotation resize now go through the typed property. - `try! SwiftMessages.viewFromNib(named:)` wrapped in do/try/catch with a graceful bail + log. Also tightened the surrounding force-unwraps on `indexPathForItem` and array indexing to guards. - Pin text field bottom to `keyboardAccessory.topAnchor` in both EditingController and WriteNoteController. That lets us drop the keyboardWillShow/Hide observers entirely — Auto Layout + the accessory's `keyboardLayoutGuide.topAnchor` pin handle keyboard tracking without manual contentInset math or alpha fades. - Audited `setNeedsStatusBarAppearanceUpdate()` calls: the child-notifies-root pattern used here is load-bearing; no removals. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… tags, dead notifications" This reverts commit f5b744c.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Foundational refresh to get the project building cleanly on Xcode 26 / iOS 26 and align with modern iOS conventions. The project hadn't been updated since ~2022 and didn't compile on current Xcode.
Dependency & build
Podfile,Podfile.lock,Pods/, andJotify.xcworkspaceremoved.https://jotifyapp.com/?invitedby=<uid>URL.UIButton.Configurationand per-VC navigation appearance APIs, and cleared six deadif #available(iOS 14, *)branches.iOS 26 rendering fixes
navigationItemappearance overUINavigationBar.standardAppearance).configureNavigationBar(bgColor:)moved fromUINavigationControllerontoUIViewController.EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64Firebase-era workaround.KeyboardAccessoryViewreplacesUIToolbarfor the keyboard bar. On iOS 26+ it hosts a SwiftUI.buttonStyle(.glass)bar throughUIHostingController; on iOS <26 it keeps the UIKit stack as fallback. Pinned toview.keyboardLayoutGuide.topAnchorfor animation-in-lockstep with the keyboard.SignInWithApple{Black,White}werefinal class : UIViewRepresentable, which modern SwiftUI asserts on. Nowstructs.Native polish
UIListContentConfiguration+ the system disclosure indicator (goodbye customchevron.right.circle.fill); switch cells useaccessoryView = UISwitch().UIButton.Configuration.filled()withcornerStyle = .large.NoteCell,DetailOnboardingController,BuyPremiumControllerscale viaUIFontMetrics/preferredFont(forTextStyle:).Hacks removed
UIViewController.rootViewControllerforce-unwrap (crashed during light/dark mode transitions); now falls back toself.isNavigationBarHidden = true; isNavigationBarHidden = falseredraw trick inEditingController.NoteCollectionControllerandSettingsController.UIApplication.shared.windows.first(deprecated iOS 15) →firstKeyWindowhelper.UNNotificationPresentationOptions.alert(deprecated iOS 14) →[.banner, .list, .sound].credential(withProviderID:…)/updateEmail(to:)→ their non-deprecated iOS 26 replacements.Known follow-ups (not in this PR)
searchTextField.rightViewbut it didn't survive iOS 26's layout lifecycle. Worth revisiting.WriteNoteController/EditingControllercould migrate to pure SwiftUITextEditorfor a more unified experience; out of scope here.Test plan
+button.🤖 Generated with Claude Code