From 3551e9d97b66841f22ee8ac88ca9ebcce825fc47 Mon Sep 17 00:00:00 2001 From: Kyle Van Essen Date: Thu, 17 Sep 2026 15:22:16 -0400 Subject: [PATCH] Raise iOS deployment minimum to 27 Set the shared iOS deployment target to 27.0 and remove the obsolete iOS 26.1 tab accessory availability branch. Keep snapshot settling needed on the iOS 27 renderer while updating its documentation. --- AGENTS.md | 2 +- Package.swift | 2 +- Project.swift | 2 +- README.md | 2 +- Shared/SnapshotKit/README.md | 2 +- Shared/SnapshotKit/Sources/SnapshotCase.swift | 2 +- Shared/SnapshotKitTesting/README.md | 2 +- .../Sources/SnapshotRenderingSupport.swift | 2 +- Where/WhereUI/README.md | 9 ++++----- Where/WhereUI/Sources/MainTabs.swift | 6 +++++- .../LocationWelcomeAccessoryModifier.swift | 18 ------------------ .../Sources/Primary/LocationsView.swift | 2 +- Where/WhereUI/Sources/RootView.swift | 2 +- .../WhereUI/Sources/Shared/LocationNamer.swift | 2 +- 14 files changed, 20 insertions(+), 35 deletions(-) delete mode 100644 Where/WhereUI/Sources/Primary/LocationWelcomeAccessoryModifier.swift diff --git a/AGENTS.md b/AGENTS.md index ee1c818a3..e5471392a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -605,7 +605,7 @@ editor when changing its schema. ## Cursor Cloud specific instructions -Cloud agent VMs run **Linux**, not macOS. This repo targets **iOS 26** with +Cloud agent VMs run **Linux**, not macOS. This repo targets **iOS 27** with **Xcode 27+** and **Tuist** (macOS-only). Treat Linux as a partial dev environment. Formatting and agent sync work on Linux. Builds, tests, and running the **Where** app require macOS (as in CI on the `xcode-27` runner image). diff --git a/Package.swift b/Package.swift index 933ad2e17..499006592 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ let package = Package( name: "Stuff", defaultLocalization: "en", platforms: [ - .iOS(.v26), + .iOS("27.0"), .macOS(.v26), ], products: [ diff --git a/Project.swift b/Project.swift index ac1988acc..2970f2738 100644 --- a/Project.swift +++ b/Project.swift @@ -1,7 +1,7 @@ import ProjectDescription let destinations: Destinations = [.iPhone, .iPad] -let deployment: DeploymentTargets = .iOS("26.0") +let deployment: DeploymentTargets = .iOS("27.0") /// The Ledger menu bar app is the only native-macOS target; everything else /// stays on the shared iOS destinations above. diff --git a/README.md b/README.md index 435a75936..2b372f489 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Random apps and stuff. ## Requirements - Xcode 27+ (a full Xcode.app, not the Command Line Tools) -- iOS 26.0+ +- iOS 27.0+ - [mise](https://mise.jdx.dev) pins Tuist, SwiftFormat, and Ruby. `./ide --bootstrap` installs it for you (see below). diff --git a/Shared/SnapshotKit/README.md b/Shared/SnapshotKit/README.md index aff3e1a5a..330349b6a 100644 --- a/Shared/SnapshotKit/README.md +++ b/Shared/SnapshotKit/README.md @@ -63,7 +63,7 @@ capture + comparison pipeline lives in the sibling layout pass (`.immediate` — skips the loop, so static content captures fast). `.settledAtLeast(minDuration:)` is `.settled` with a raised minimum window, for async appearance work that starts quiet and lands after the default floor - (the iOS 26 glass toolbar/tab bar material adaptation). + (the native glass toolbar/tab bar material adaptation). Intrinsic/full-content cases also have a `measurementReadiness` axis. Its default, `.sameAsCapture`, preserves the existing behavior for content whose loaded state changes its height. Deterministically sized fixtures may use diff --git a/Shared/SnapshotKit/Sources/SnapshotCase.swift b/Shared/SnapshotKit/Sources/SnapshotCase.swift index 038ca8e8b..b49c56e28 100644 --- a/Shared/SnapshotKit/Sources/SnapshotCase.swift +++ b/Shared/SnapshotKit/Sources/SnapshotCase.swift @@ -7,7 +7,7 @@ public enum SnapshotSettle: Equatable, Sendable { case settled /// ``settled`` with a raised minimum settle window. For content whose async /// appearance work starts *quiet* and lands after the default floor — the - /// iOS 26 glass toolbar/tab bar adapts its material to the content behind it + /// native glass toolbar/tab bar adapts its material to the content behind it /// a few hundred ms after hosting, which no pixel-stability check can /// anticipate before it starts. The floor is paid on every capture of the /// case, so reserve it for cases that host such chrome. diff --git a/Shared/SnapshotKitTesting/README.md b/Shared/SnapshotKitTesting/README.md index d6c8b3786..ac8d05efd 100644 --- a/Shared/SnapshotKitTesting/README.md +++ b/Shared/SnapshotKitTesting/README.md @@ -47,7 +47,7 @@ re-exports `SnapshotKit` and `SnapshotTesting`, so a test author needs a single `onReadyToSnapshot`). `.settledAtLeast(minDuration:)` raises the loop's minimum window for async appearance work that starts quiet and lands after the default floor (the - iOS 26 glass toolbar/tab bar material adaptation). `.immediate` skips the + native glass toolbar/tab bar material adaptation). `.immediate` skips the loop for content that's fully renderable after a layout pass. Intrinsic and full-content cases can independently set `measurementReadiness: .immediate` when their fixture's height is synchronous: only the sizing probe skips its diff --git a/Shared/SnapshotKitTesting/Sources/SnapshotRenderingSupport.swift b/Shared/SnapshotKitTesting/Sources/SnapshotRenderingSupport.swift index ae1b831df..c2a604d87 100644 --- a/Shared/SnapshotKitTesting/Sources/SnapshotRenderingSupport.swift +++ b/Shared/SnapshotKitTesting/Sources/SnapshotRenderingSupport.swift @@ -134,7 +134,7 @@ func settleForCapture( /// quiesces — e.g. a repeating pulse — doesn't hang the capture). /// /// `minDuration` also covers async appearance work that starts *quiet* and lands -/// late: the iOS 26 glass toolbar/tab bar adapts its material to the content +/// late: the native glass toolbar/tab bar adapts its material to the content /// behind it a few hundred ms after hosting, so a floor below that captures the /// pre-adaptation glass (seen on `primary`/`root` snapshots when the floor was /// 50ms). diff --git a/Where/WhereUI/README.md b/Where/WhereUI/README.md index 90ef8a0e8..cc20c5760 100644 --- a/Where/WhereUI/README.md +++ b/Where/WhereUI/README.md @@ -142,11 +142,10 @@ the feature [`Where/AGENTS.md`](../AGENTS.md) and this module's The scrim fades independently. Reduce Motion uses a short fade for both layers. Debug builds include **Reset Welcome Card** in Settings > Appearance beside the welcome-card toggle. The reset clears the saved region so the next foreground activation can show the card again. - `MainTabs` requests one bounded fix on each active-scene entry. On iOS 26.1 - and later, it shows a native tab-bar accessory after one second; denied or - restricted access and disabled Precise Location remain actionable there, - while transient or low-confidence failures disappear. iOS 26.0 still runs - the lookup and presents the welcome card, but does not show the accessory. + `MainTabs` requests one bounded fix on each active-scene entry. It shows a + native tab-bar accessory after one second; denied or restricted access and + disabled Precise Location remain actionable there, while transient or + low-confidence failures disappear. Welcome cards must be enabled, and the device must confidently resolve a tracked region with recording active. diff --git a/Where/WhereUI/Sources/MainTabs.swift b/Where/WhereUI/Sources/MainTabs.swift index 0c66cca2a..9a4a3769d 100644 --- a/Where/WhereUI/Sources/MainTabs.swift +++ b/Where/WhereUI/Sources/MainTabs.swift @@ -99,7 +99,11 @@ struct MainTabs: View { .badge(recordingWarning.isPresented ? 1 : 0) } .accessibilityHidden(welcomePresentation != nil) - .modifier(LocationWelcomeAccessoryModifier(accessory: welcomeAccessory)) + .tabViewBottomAccessory(isEnabled: welcomeAccessory != nil) { + if let welcomeAccessory { + LocationWelcomeStatusAccessory(accessory: welcomeAccessory) + } + } // Keep the tab bar fixed — don't minimize it as content scrolls. .tabBarMinimizeBehavior(.never) .overlay { diff --git a/Where/WhereUI/Sources/Primary/LocationWelcomeAccessoryModifier.swift b/Where/WhereUI/Sources/Primary/LocationWelcomeAccessoryModifier.swift deleted file mode 100644 index 90400e0df..000000000 --- a/Where/WhereUI/Sources/Primary/LocationWelcomeAccessoryModifier.swift +++ /dev/null @@ -1,18 +0,0 @@ -import SwiftUI - -/// Adds the system tab accessory only while live-region status is visible on iOS 26.1 and later. -struct LocationWelcomeAccessoryModifier: ViewModifier { - let accessory: LocationWelcomeModel.Accessory? - - func body(content: Content) -> some View { - if #available(iOS 26.1, *) { - content.tabViewBottomAccessory(isEnabled: accessory != nil) { - if let accessory { - LocationWelcomeStatusAccessory(accessory: accessory) - } - } - } else { - content - } - } -} diff --git a/Where/WhereUI/Sources/Primary/LocationsView.swift b/Where/WhereUI/Sources/Primary/LocationsView.swift index a57e99e7a..4b1a0f42a 100644 --- a/Where/WhereUI/Sources/Primary/LocationsView.swift +++ b/Where/WhereUI/Sources/Primary/LocationsView.swift @@ -334,7 +334,7 @@ private struct ResolveToolbarLabel: View { #if DEBUG extension LocationsView: SnapshotProviding { - /// The raised settle floor on `Loaded` outlasts the iOS 26 glass toolbar + /// The raised settle floor on `Loaded` outlasts the native glass toolbar /// material adaptation (seen pre-adaptation once on the equivalent /// pre-split screen) — same mechanism as `RootView.LoggedIn`. static var snapshots: [SnapshotCase] { diff --git a/Where/WhereUI/Sources/RootView.swift b/Where/WhereUI/Sources/RootView.swift index 6c41eaef1..ce08234ec 100644 --- a/Where/WhereUI/Sources/RootView.swift +++ b/Where/WhereUI/Sources/RootView.swift @@ -342,7 +342,7 @@ public struct RootView: View { /// idempotent and awaits the in-flight drive, a deterministic "reached /// `.ready`" signal — so the raised settle floor only has to outlast the /// post-ready tail: `MainTabs`' `.task` activation (empty-store re-pull + - /// Resolve badge) and the iOS 26 glass toolbar/tab bar material + /// Resolve badge) and the native glass toolbar/tab bar material /// adaptation, which starts quiet a few hundred ms after the chrome /// hosts. Those have no reachable completion signal (the scene's report /// model is private to `MainTabs`; the adaptation has no public diff --git a/Where/WhereUI/Sources/Shared/LocationNamer.swift b/Where/WhereUI/Sources/Shared/LocationNamer.swift index 048ab1e08..68e16f9a6 100644 --- a/Where/WhereUI/Sources/Shared/LocationNamer.swift +++ b/Where/WhereUI/Sources/Shared/LocationNamer.swift @@ -23,7 +23,7 @@ struct PlaceComponents: Equatable { } extension PlaceComponents { - /// iOS 26's MapKit geocoder folds the old `CLPlacemark` fields into + /// MapKit's geocoder folds the old `CLPlacemark` fields into /// formatted strings; `cityName` and `regionName` (the country) are the two /// pieces this compact teaser needs. init(_ representations: MKAddressRepresentations) {