Summary
Make Shellbee available on iOS 17 and up. Currently the app requires iOS 26, which excludes most of the installed base.
Approach
Drop the deployment target to iOS 17 and gate iOS 18 / iOS 26 APIs behind if #available so newer-OS users still get the native look (Liquid Glass, minimized search toolbar, mesh gradients, indefinite symbol effects, the new Tab { } builder), while iOS 17 users get sensible old-school equivalents.
Implementation
- New
Shellbee/Shared/Compat/iOS26Compat.swift with one-line helpers:
glassEffectIfAvailable(in:) → .glassEffect (26) / .ultraThinMaterial background (17)
glassButtonStyleIfAvailable() / glassProminentButtonStyleIfAvailable() → .glass / .glassProminent (26) / .bordered / .borderedProminent (17)
minimizeSearchToolbarIfAvailable() → .searchToolbarBehavior(.minimize) (26) / no-op (17)
bounceSymbolEffectIfAvailable() → indefinite .symbolEffect(.bounce) (18) / no-op (17, since BounceSymbolEffect only conforms to IndefiniteSymbolEffect on 18+)
- Call sites migrated to helpers:
LightControlCard, FastTrackBanner, InAppNotificationBanner, GroupListView, LogsView, DeviceListView, DocBrowserView, OnboardingView, OnboardingTestPage.
HomeBackgroundGradient: MeshGradient (iOS 18+) with a LinearGradient fallback (file-local).
MainTabView: split into if #available(iOS 18, *) for the new Tab { } builder vs. the classic .tabItem API on iOS 17.
- Widgets (
InterviewActivityWidget, ConnectionActivityWidget): replaced .repeat(.continuous) (iOS 18+) with .repeating (iOS 17+).
Shared/Compat/iOS26Compat.swift added to ShellbeeWidgetsExtension membershipExceptions (widget target doesn't need it and chokes on the bounce indefinite helper).
Test plan
Summary
Make Shellbee available on iOS 17 and up. Currently the app requires iOS 26, which excludes most of the installed base.
Approach
Drop the deployment target to iOS 17 and gate iOS 18 / iOS 26 APIs behind
if #availableso newer-OS users still get the native look (Liquid Glass, minimized search toolbar, mesh gradients, indefinite symbol effects, the newTab { }builder), while iOS 17 users get sensible old-school equivalents.Implementation
Shellbee/Shared/Compat/iOS26Compat.swiftwith one-line helpers:glassEffectIfAvailable(in:)→.glassEffect(26) /.ultraThinMaterialbackground (17)glassButtonStyleIfAvailable()/glassProminentButtonStyleIfAvailable()→.glass/.glassProminent(26) /.bordered/.borderedProminent(17)minimizeSearchToolbarIfAvailable()→.searchToolbarBehavior(.minimize)(26) / no-op (17)bounceSymbolEffectIfAvailable()→ indefinite.symbolEffect(.bounce)(18) / no-op (17, sinceBounceSymbolEffectonly conforms toIndefiniteSymbolEffecton 18+)LightControlCard,FastTrackBanner,InAppNotificationBanner,GroupListView,LogsView,DeviceListView,DocBrowserView,OnboardingView,OnboardingTestPage.HomeBackgroundGradient:MeshGradient(iOS 18+) with aLinearGradientfallback (file-local).MainTabView: split intoif #available(iOS 18, *)for the newTab { }builder vs. the classic.tabItemAPI on iOS 17.InterviewActivityWidget,ConnectionActivityWidget): replaced.repeat(.continuous)(iOS 18+) with.repeating(iOS 17+).Shared/Compat/iOS26Compat.swiftadded toShellbeeWidgetsExtensionmembershipExceptions(widget target doesn't need it and chokes on thebounceindefinite helper).Test plan
.tabItem) doesn't visually break the in-app notification overlay positioning