Skip to content

Require iOS 15 and Xcode 26 (Swift 6.2) - #532

Open
yusuftor wants to merge 7 commits into
developfrom
chore/min-ios15-swift62
Open

yusuftor wants to merge 7 commits into
developfrom
chore/min-ios15-swift62

Conversation

@yusuftor

@yusuftor yusuftor commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Changes in this pull request

Since April 28, 2026, Apple has required App Store Connect uploads to be built with the iOS 26 SDK, so any app picking up a new SDK release is already on Xcode 26. This PR raises the SDK's minimums to match, then removes the code that only existed to support older versions.

Minimums (commit 1)

  • Toolchain: swift-tools-version goes from 5.5 to 6.2 (Xcode 26+). The package pins swiftLanguageModes: [.v5], so strict concurrency stays off; moving to Swift 6 language mode can be a separate change.
  • Deployment targets: iOS goes from 13 to 15 in Package.swift, the podspec, project.yml and the Xcode project.
  • Other SwiftPM platforms: macOS goes from 10.12 to 12, because SwiftPM 6.2 deprecates anything older. watchOS goes from 6.2 to 8, to pair with iOS 15. tvOS isn't declared, because the SDK can't build for it (WebKit, SafariServices and Superscript have no tvOS build).
  • Manifest syntax: the Superscript dependency uses exact:, because .exact(...) isn't available in the 6.2 manifest.
  • Podspec language version: swift_versions stays at 5.5. In CocoaPods it sets the Swift language version, not the compiler, so setting it to 6.2 would force Swift 6 language mode on pod users.

Dead code removed

  • Compiler checks: #if compiler(...) checks for versions below 6.2, the swift(<5.7) workaround that stored the SK2 Product as Any, xcode12.py and its // ignore-xcode-12 markers. The compiler(>=6.3.2) checks for the iOS 26.4 StoreKit APIs stay. (commit 1)
  • Availability checks (commit 2): every @available/#available check at or below iOS 15 / Mac Catalyst 15 / macOS 12 / watchOS 8, across 71 files, along with the fallback branches that only ran on older OSes:
    • The default storeKitVersion is now always StoreKit 2. Before, it was StoreKit 1 on iOS 13/14.
    • Receipt validation no longer has a SecTrustEvaluate fallback.
    • The pre-iOS 14 photo-permission APIs are gone, and camera/tracking permissions are no longer reported as "unsupported" on old OSes.
    • The Test Mode action-sheet fallbacks for the UIMenu buttons are gone, along with their helpers.
    • The guard #available(...) else { return } early returns in the tests are gone.
    • Checks for iOS 16 and later are unchanged.
  • Small fixes:
    • interfaceType handles .vision inside the switch, which clears the existing "switch must be exhaustive" warning.
    • keyboardDismissMode in the debugger's paywall picker is now guarded with #if !os(visionOS). It was already breaking the visionOS build on develop.

Release: version bumped to 4.18.0, with a CHANGELOG "Breaking Changes" entry and a toolchain section in CLAUDE.md, including a rule not to reintroduce these checks.

Usage data behind the decision (Superwall events, last 24h, iOS apps on SDK 4.10.6+, grouped by the compilerVersion device attribute):

  • Swift 6.2 or later (Xcode 26+): about 1,650+ apps and about 19M users.
  • Swift 6.0–6.1 (Xcode 16): 173 apps and about 43k users. These are older builds already installed on phones, and they can't ship an update without moving to Xcode 26.
  • iOS versions below 15 account for about 0.005% of users.

Checklist

  • All unit tests pass. (1,091 tests in 110 suites, on an iOS 27 simulator. Note: scripts/test.sh targets an "iPhone 16" simulator, which fails to launch because the test target defaults to iOS 27. That was already the case before this PR.)
  • All UI tests pass.
  • Demo project builds and runs on iOS. (The SDK package builds for iOS Simulator; I didn't run the demo app.)
  • Demo project builds and runs on Mac Catalyst. (The SDK package builds for Mac Catalyst; I didn't run the demo app.)
  • Demo project builds and runs on visionOS. (The SDK package builds for visionOS Simulator; I didn't run the demo app.)
  • I added/updated tests or detailed why my change isn't tested. (This only changes build configuration and removes branches that can no longer run; the existing suite covers the remaining paths.)
  • I added an entry to the CHANGELOG.md for any breaking changes, enhancements, or bug fixes.
  • I have run swiftlint in the main directory and fixed any issues. (The 2 remaining warnings were already there.)
  • I have updated the SDK documentation as well as the online docs.
  • I have reviewed the contributing guide

🤖 Generated with Claude Code

RetriggerConfidence Score: 5/5

The PR appears safe to merge, with both previous findings fully addressed and no new actionable failures identified.

Summary

Raises the SDK’s minimum toolchain and supported platform versions, removes compatibility paths that are now unreachable, and updates release documentation.

  • Requires Swift 6.2/Xcode 26 while retaining Swift 5 language mode.
  • Raises SwiftPM deployment floors to iOS 15, macOS 12, and watchOS 8.
  • Simplifies StoreKit, permissions, attribution, receipt-validation, and UI code by removing obsolete availability/compiler branches.
  • Adds visionOS package-build coverage and ensures package-manifest changes trigger platform CI.
  • The previously reported camera dead-code issue is fixed and its thread is resolved.

Reviews (2) · Last reviewed commit: "Address review: tvOS floor, dead Catalys..."

yusuftor and others added 2 commits September 23, 2026 14:59
Apple has required the iOS 26 SDK for App Store Connect uploads since
April 28, 2026, so no shipping app can build a new SDK release with an
older Xcode. Raise swift-tools-version to 6.2 (keeping the Swift 5
language mode), the iOS deployment target to 15, and macOS to 12, the
oldest the 6.2 manifest supports. Drop the compiler checks that are now
always true, the Swift < 5.7 StoreKit product workaround, and the
Xcode 12 script.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With iOS 15, macOS 12 and watchOS 8 as the floor, every
@available/#available check at or below them is always true. Drop
them, their else branches (StoreKit 1 default on iOS 13/14,
SecTrustEvaluate, action-sheet menus, pre-iOS 14 photo permission
APIs), the now-unused action sheet helpers, and the matching test
guards. Also guard keyboardDismissMode, which is unavailable on
visionOS and broke that build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yusuftor yusuftor added the docs-required Ships a customer-facing change that needs a superwall/docs update label Sep 23, 2026
@yusuftor

Copy link
Copy Markdown
Collaborator Author

📚 Docs required

What changed for customers: SuperwallKit 4.18.0 needs iOS 15+ (was iOS 13) and Xcode 26 / Swift 6.2 to build (was swift-tools 5.5). SwiftPM also needs macOS 12+ and watchOS 8+. On every supported OS, storeKitVersion now defaults to StoreKit 2.

Why this needs docs: Customers set their deployment target and toolchain against these supported versions (rule 6), and the new floor is a prerequisite they must meet before upgrading (rule 4). The docs also still describe a StoreKit 1 fallback on older iOS versions, and that default has changed (rule 3).

Coverage today:

Without a docs page this change also gets no changelog entry: superwall/docs publishes a daily GitHub Release that superwall.com renders at /changelog, and every entry must link to a live docs page. Undocumented means invisible to customers.

Prompt for the docs agent. Run in superwall/docs
Document a change that shipped in superwall/Superwall-iOS#532: raise iOS SDK minimums to iOS 15 / Xcode 26 (SuperwallKit 4.18.0).

What shipped, in customer terms:
SuperwallKit 4.18.0 raises the minimum deployment target from iOS 13 to iOS 15, for both SwiftPM and CocoaPods.
Building it now needs Xcode 26 or later, because the package manifest uses swift-tools-version 6.2. Apple has required the iOS 26 SDK for App Store uploads since April 28, 2026.
The package stays in Swift 5 language mode, so customers do not have to adopt Swift 6 strict concurrency.
The SwiftPM minimums for other platforms are now macOS 12 and watchOS 8.
The public API is unchanged. Because iOS 13/14 are no longer supported, the default `storeKitVersion` is now always StoreKit 2. There is no StoreKit 1 fallback on older OS versions any more, though customers can still opt in to `.storeKit1` explicitly.

Setup or prerequisites a customer must complete:
- The app's deployment target must be iOS 15.0 or higher. For CocoaPods, that means `platform :ios, '15.0'` or higher in the Podfile.
- Build with Xcode 26 or later.
- Apps that must keep supporting iOS 13/14 stay on SuperwallKit 4.17.x.

Where it belongs:
- content/docs/ios/quickstart/install.mdx: add a "Requirements" section near the top, after Overview. List iOS 15.0+, Xcode 26+ (Swift 6.2 toolchain), and for SwiftPM macOS 12+ / watchOS 8+. Note that 4.18.0 raised these minimums and that apps needing iOS 13/14 should pin 4.17.x.
- content/docs/ios/sdk-reference/SuperwallOptions.mdx:
  - L15: remove the "falls back to StoreKit 1 on older versions" wording.
  - L52: change the storeKitVersion default to "StoreKit 2".
  - Also check the ~L212 example comment "for better performance on iOS 15+".
- content/docs/ios/guides/migrations/migrating-to-v4.mdx L34: optionally clarify that "on iOS 15+" now covers every supported OS as of 4.18.0.
- content/docs/ios/changelog.mdx: add the 4.18.0 entry, if it is not already synced from the SDK CHANGELOG.

Source of truth. Read these before writing:
- superwall/Superwall-iOS#532 diff: gh pr diff 532 --repo superwall/Superwall-iOS --patch
- Key files: Package.swift, SuperwallKit.podspec, CHANGELOG.md, Sources/SuperwallKit/Config/Options/SuperwallOptions.swift, README.md

Constraints:
- Match the voice and structure of neighbouring pages under content/docs/**.
- Use the Fumadocs TypeTable for parameters and types; never <ParamTable>.
- If you add a page, add it to the folder's meta.json.
- Run `bun run build:cf` and `bun test` before opening the PR.
- Open a PR against superwall/docs referencing superwall/Superwall-iOS#532. Do not deploy.

Flagged by the docs-required skill. If this is wrong, remove the label and say why in a reply so the skill's calibration set can be corrected.

Comment thread Package.swift
Comment thread Sources/SuperwallKit/Permissions/Handlers/PermissionHandler+Camera.swift Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The code cleanup is thorough and correct — a full sweep of Sources/ finds no remaining @available/#available below the new floor. Two things to settle before merging: the version number chosen for a breaking deployment-target raise, and a dead-code leftover in the Mac Catalyst camera path.

Reviewed changes — both commits on chore/min-ios15-swift62, covering the manifest/toolchain bump and the 71-file availability cleanup.

  • Toolchain floor — swift-tools-version 5.5 → 6.2 with swiftLanguageModes: [.v5] pinned, so strict concurrency stays off; the Superscript dependency moves to the exact: label form required by the 6.2 manifest API.
  • Platform floor — iOS 13 → 15, macOS 10.12 → 12, watchOS 6.2 → 8, kept in sync across Package.swift, SuperwallKit.podspec, project.yml and project.pbxproj.
  • Compiler-gate removal — #if compiler(...) checks below 6.2, the swift(<5.7) Any-boxing workaround in SK2StoreProduct, and xcode12.py with its // ignore-xcode-12 markers are gone; compiler(>=6.3.2) gates for the iOS 26.4 StoreKit APIs are correctly retained.
  • Availability cleanup — every @available/#available at or below the new floor, plus the fallback branches only reachable on older OSes: StoreKit 1 default on iOS 13/14, the SecTrustEvaluate receipt path, pre-iOS-14 photo permissions, and the Test Mode action-sheet fallbacks for UIMenu buttons.
  • Small fixes — interfaceType handles .vision inside the switch, and keyboardDismissMode in the debugger picker is now #if !os(visionOS)-guarded.
  • Release plumbing — 4.17.0 → 4.18.0 across Constants.swift, the podspec and CHANGELOG.md, plus a new "Minimum Toolchain and Platforms" section in CLAUDE.md.

I verified the manifest parses (swift package dump-package reports ios 15.0 / macos 12.0 / watchos 8.0, swiftLanguageVersions: ["5"], toolsVersion 6.2.0) and that no CI workflow pins an Xcode below 26 — every macOS job is macos-26 with latest-stable.

⚠️ A breaking deployment-target raise is shipping as a minor version

4.17.0 → 4.18.0 for a change whose own CHANGELOG.md entry is filed under "### Breaking Changes". SwiftPM's default requirement is .upToNextMajor, so every integrator pinned at from: "4.x" resolves into this release automatically — and any of them still targeting iOS 13 or 14 gets a hard resolution failure (requires minimum platform version 15.0) from a package update they didn't ask for. A major tag is the mechanism that stops that. Repo precedent agrees: the last minimum-iOS raise shipped in 3.0.0, also under "### Breaking Changes".

Technical details
# Version number for the iOS 15 / Xcode 26 floor

## Affected sites
- `Sources/SuperwallKit/Misc/Constants.swift:21` — `4.18.0`
- `SuperwallKit.podspec:4` — `s.version = "4.18.0"`
- `CHANGELOG.md:5-9` — `## 4.18.0` with a `### Breaking Changes` heading

## Context already verified
- `master` and `develop` both carried `4.17.0` before this PR, so per `CLAUDE.md`
  opening a new version section (rather than appending to a staged one) was correct.
  The only question is which number.
- `CHANGELOG.md:1368` — "Sets the minimum iOS version to iOS 13." shipped in `3.0.0`.

## Required outcome
- A deliberate, recorded decision on major vs minor. If `5.0.0`, bump all three
  files together per `CLAUDE.md`. If `4.18.0` stands, say in the changelog entry
  why a minor was chosen so integrators reading it understand the resolution
  failure they may hit.

## Open questions for the human
- Is there a separate v5 milestone this would collide with?
- Does the release process publish a migration note, and does the online
  documentation's stated minimum get updated alongside the tag? The PR checklist
  leaves "I have updated the SDK documentation as well as the online docs" unchecked.

ℹ️ One iOS 13 fallback survived the sweep, outside the diff

Sources/SuperwallKit/Permissions/Handlers/Location/LocationPermissionDelegate.swift still declares locationManager(_:didChangeAuthorization:) — the pre-iOS 14 CoreLocation delegate selector — alongside its doc comments "Implements both iOS 14+ and iOS 13 delegate methods dynamically" and "iOS 13 and earlier delegate method". At an iOS 15 floor CLLocationManager only ever dispatches locationManagerDidChangeAuthorization(_:), so the older selector is unreachable. Worth noting that the file's header comment explains these @objc selectors are deliberately present in the binary's Objective-C metadata, so this is a considered removal rather than a blind delete.

Technical details
# Residual pre-iOS 14 CoreLocation delegate path

## Affected sites
- `Sources/SuperwallKit/Permissions/Handlers/Location/LocationPermissionDelegate.swift:11`
  — class doc comment "Implements both iOS 14+ and iOS 13 delegate methods dynamically"
- `Sources/SuperwallKit/Permissions/Handlers/Location/LocationPermissionDelegate.swift:37-45`
  — `/// iOS 13 and earlier delegate method` + the `#if !os(visionOS)` guarded
    `@objc func locationManager(_:didChangeAuthorization:)`
- `Sources/SuperwallKit/Permissions/Handlers/Location/LocationPermissionDelegate.swift:49`
  — comment "Try instance property first (iOS 14+)", whose "first" framing implies a
    fallback that no longer needs to exist

## Required outcome
- Either drop the iOS 13 selector and realign the comments to the iOS 15 floor, or
  leave it and record why (the header comment's `scan-privacy-signatures.sh` rationale
  is about which selectors are *acceptable* to expose, not about which are still needed).

## Open questions for the human
- Does removing an `@objc` selector from the binary's metadata interact with the
  privacy-signature scanning this file's header describes?

ℹ️ Nitpicks

  • Tests/SuperwallKitTests/StoreKit/Products/StoreProduct/SK2PriceFormatRoundingTests.swift:23-25 — the comment still says "SuperwallKit ships with an iOS 13 minimum so we guard at runtime" after the guard was removed.
  • Tests/SuperwallKitTests/StoreKit/Products/SK2StoreProductCyclesTests.swift:17-19 — "each test guards with #available before calling it" no longer describes the tests.
  • Sources/SuperwallKit/StoreKit/Transactions/Purchasing/PurchasingCoordinator.swift:69 — "If on iOS 15+, try and get latest transaction using SK2" is now unconditional.
  • Sources/SuperwallKit/Misc/Extensions/SystemInfo+NotificationName.swift:38 — the dyld-crash workaround comment says "watchOS 7.0..<9.0"; the reachable range is now 8.0..<9.0.
  • Tests/SuperwallKitTests/Paywall/View Controller/Web View/Message Handling/PaywallMessageHandlerDelegateMock.swift:29-39 — a #if compiler(>=6.0) with a dead #else branch escaped the compiler-gate sweep, which the new CLAUDE.md rule now forbids.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using Claude Opus | 𝕏

Comment thread Sources/SuperwallKit/Permissions/Handlers/PermissionHandler+Camera.swift Outdated
Declare tvOS 15 so the unguarded StoreKit 2 declarations still compile
for tvOS consumers, remove the unreachable `.unsupported` return and
empty #if left in the camera permission handler, and build visionOS in
CI through Package.swift (the xcodegen project is iOS-only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yusuftor

Copy link
Copy Markdown
Collaborator Author

Re the version question in the Pullfrog review: 4.18.0 is intentional. Every App Store Connect upload already needs Xcode 26 / the iOS 26 SDK, so no shipping app can take this release without already meeting its minimums. Apps still on iOS 13/14 can stay on 4.17.x.

The other review findings (tvOS floor, dead Catalyst camera code) are fixed in 8fc6fce, which also adds a visionOS build to build-platforms.yml.

Drop the pre-iOS 14 locationManager(_:didChangeAuthorization:)
callback and its test (CLLocationManager only dispatches
locationManagerDidChangeAuthorization(_:) on iOS 15+), and update the
privacy-signature notes that listed it. Remove a test-only
#if compiler(>=6.0) gate, refresh comments that still described
iOS 13/14 or runtime #available guards, and explain in the changelog
why the release is a minor and how iOS 13/14 apps can stay on 4.17.x.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yusuftor

Copy link
Copy Markdown
Collaborator Author

Addressed the rest of the Pullfrog review in 6e264da:

  • Version: 4.18.0 stays. The changelog entry now explains why it's a minor release, and tells apps that still support iOS 13/14 to pin 4.17.x (.upToNextMinor(from: "4.17.0") in SwiftPM, ~> 4.17.0 in CocoaPods).
  • iOS 13 location callback: removed locationManager(_:didChangeAuthorization:) and its test, since CLLocationManager only dispatches locationManagerDidChangeAuthorization(_:) on iOS 15+. The class doc comment and the scan-privacy-signatures.sh note now name only the remaining selector. Removing a selector only takes a name out of the binary's metadata, so the privacy scan isn't affected.
  • Nitpicks: removed the stale comments in SK2PriceFormatRoundingTests/SK2StoreProductCyclesTests and updated the ones in PurchasingCoordinator and the watchOS range, removed the test-only #if compiler(>=6.0) in FakeWebView, and updated two other comments that still referred to iOS 14.

All 1,090 tests pass. On the previous push, CI's Mac Catalyst and visionOS builds both passed.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No blocking issues — the prior review's camera dead-code comment is fully addressed and CI is green on all three platform jobs. One observation on the tvOS floor inline.

Reviewed changes — the single new commit 8fc6fce since the prior pullfrog review at 4136dc8, a 5-file response to review feedback.

  • Collapsed the Mac Catalyst camera conditionals — PermissionHandler+Camera.swift now has no #if targetEnvironment(macCatalyst) at all; the unreachable return .unsupported and the empty #if/#endif pair are both gone, exactly as suggested.
  • Declared a tvOS 15 floor — .tvOS(.v15) in Package.swift, echoed in the CHANGELOG.md breaking-changes entry and the CLAUDE.md toolchain section.
  • Added a build-visionos CI job — macos-26 + latest-stable, a conditional xcodebuild -downloadPlatform visionOS, then rm -r SuperwallKit.xcodeproj so the build goes through Package.swift (the xcodegen project is iOS-only). Package.swift was also added to the workflow's push: paths filter.

I confirmed build-visionos, build-maccatalyst and Package-SwiftLint all pass on 8fc6fce, which also proves the libcel xcframework behind the Superscript dependency ships visionOS slices.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using Claude Opus | 𝕏

Comment thread Package.swift Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No new issues in this delta — every remaining item from the previous review is addressed. One changelog-prose nitpick below.

Reviewed changes — the single new commit 6e264da since the prior pullfrog review at 8fc6fce, an 11-file cleanup pass responding to review feedback.

  • Dropped the pre-iOS 14 CoreLocation selector — LocationPermissionDelegate.locationManager(_:didChangeAuthorization:), its test, and the class/scan-privacy-signatures.sh notes that described two selectors now describe one. At the iOS 15 floor CLLocationManager only ever dispatches locationManagerDidChangeAuthorization(_:), and no reference to the old selector survives anywhere in Sources/, Tests/ or scripts/.
  • Removed the last compiler gate below the floor — PaywallMessageHandlerDelegateMock's #if compiler(>=6.0) and its dead #else branch are gone, keeping the @MainActor completion-handler signature.
  • Refreshed six stale comments — AttributionFetcher (pre-14.5 IDFA/ATT clause), DebugPaywallPickerViewController ("on iOS 14+"), SystemInfo+NotificationName ("watchOS 7.0..<9.0" → "8.0..<9.0"), PurchasingCoordinator ("If on iOS 15+"), SK2StoreProductCyclesTests and SK2PriceFormatRoundingTests (both described #available guards that no longer exist).
  • Recorded the minor-version decision in the changelog — CHANGELOG.md:10 now explains why 4.18.0 rather than 5.0.0 and tells iOS 13/14 apps to pin 4.17.x. I verified both forms resolve the intended range: .upToNextMinor(from: "4.17.0") and ~> 4.17.0 each give 4.17.0..<4.18.0.

MockLocationManager is still exercised by the five surviving location tests, so the test-file deletion left nothing dangling.

ℹ️ Nitpicks

  • CHANGELOG.md:10 — "every App Store Connect upload already needs Xcode 26, so any app that can ship an update already meets the new Xcode requirement" argues for the toolchain bump, but the resolution failure described in the very next sentence comes from the deployment-target bump, which an Xcode 26 app can still fail. The escape-hatch guidance that follows is accurate and complete; only the lead-in reasoning skips a step.

Pullfrog  | Fix it ➔ | View workflow run | Using Claude Opus | 𝕏

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No blocking issues. The merge resolved cleanly. It did bring in Customer Center code that re-adds the kind of availability checks this PR sets out to remove.

Reviewed changes: the single new commit 542f143 since the prior pullfrog review at 6e264da, a merge of develop that brought in the Customer Center feature, which develop had staged under its own 4.18.0.

  • Merged the two 4.18.0 changelog sections: one ## 4.18.0 heading, "Breaking Changes" first, then the Customer Center Enhancements and Fixes. The Customer Center entry's "Requires iOS 15+." was dropped because the SDK floor now makes it redundant.
  • Kept the version consistent: Constants.swift, the podspec and CHANGELOG.md all read 4.18.0, so nothing needed bumping again.

build-visionos and both SwiftLint jobs are green on 542f143, so the merged Customer Center code builds under the new manifest. run-tests and build-maccatalyst were still pending when I wrote this.

ℹ️ The merge brings back sub-floor availability checks via Customer Center

The branch no longer matches the PR description ("every @available/#available check at or below iOS 15 … removed") or the new CLAUDE.md rule. develop's Customer Center code adds about 70 @available(iOS 15.0, *) / #available(iOS 15.0, *) checks under Sources/, one @available(iOS 14.0, *), and 94 more in tests. None of these lines are in the PR diff because they live on the base branch, so they can't be anchored inline. At the new floor they're all no-ops, and DependencyContainer carries an Any? boxing workaround with the same shape as the swift(<5.7) hack this PR deleted from SK2StoreProduct.

Technical details
# Customer Center carries availability checks below the iOS 15 floor

## Affected sites
- `Sources/SuperwallKit/Dependencies/DependencyContainer.swift:51-71` — `private var _customerCenterManager: Any?` plus an `@available(iOS 15.0, *)` typed accessor that casts it back; the comment justifies it purely by availability, which the floor now guarantees
- `Sources/SuperwallKit/Superwall+CustomerCenter.swift:25,53,70` — `@available(iOS 15.0, *)` on the public `presentCustomerCenter` / `dismissCustomerCenter` / ObjC entry points
- `Sources/SuperwallKit/CustomerCenter/ViewModel/CustomerCenterDependencies.swift:87,96` — `if #available(iOS 15.0, *), let …` conditions
- `Sources/SuperwallKit/CustomerCenter/Logic/AppStoreVersionLookup.swift:76-79` — `if #available(iOS 15.0, *) { … }` followed by an unreachable `return nil`
- `Sources/SuperwallKit/CustomerCenter/Models/CustomerCenterConfiguration+Appearance.swift:61` — `@available(iOS 14.0, *)`
- ~30 other files under `Sources/SuperwallKit/CustomerCenter/**` with type-level `@available(iOS 15.0, *)`
- 11 files under `Tests/SuperwallKitTests/CustomerCenter/**` (94 annotations)

## Required outcome
- Either sweep these in this PR so the branch matches its description and the `CLAUDE.md` rule, or explicitly defer the sweep to a follow-up. Checks for iOS 16 and later (e.g. `AppStoreVersionLookup.swift:69`, `CountryCode.swift:23`, the iOS 17 ones in `CustomerCenterSheets.swift` / `CustomerCenterViewModel.swift`) stay.

## Suggested approach (optional)
- `DependencyContainer`: type the backing store as `CustomerCenterManager?` and drop the cast and the availability comment.
- Drop the `#available(iOS 15.0, *)` clause from compound conditions, and collapse `if #available(iOS 15.0, *) { X }; return nil` to `X`.

## Open questions for the human
- Sweep here, or in a follow-up? The Customer Center PR is fresh on `develop`, so a separate commit may be easier to review.

ℹ️ Nitpicks

  • Sources/SuperwallKit/Config/Options/SuperwallOptions.swift:129-130: the public doc on storeKitVersion still says "StoreKit 2 by default if the app is running on iOS 15+, otherwise it will fallback to StoreKit 1". The default has been unconditionally StoreKit 2 since 4136dc8. This comment predates this delta, but it's customer-facing and was missed by earlier sweeps.

Pullfrog  | Fix it ➔ | View workflow run | Using Claude Opus | 𝕏

yusuftor and others added 2 commits September 29, 2026 16:48
Also stop saying the SDK falls back to StoreKit 1 below iOS 15.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. Both open items from earlier reviews are fixed, and the branch matches its description again.

Reviewed changes: the two commits since the prior pullfrog review at 542f143.

  • Removed the Customer Center availability checks below iOS 15: every @available(iOS 15.0, *) / #available(iOS 15.0, *) and the one @available(iOS 14.0, *) is gone from Sources/SuperwallKit/CustomerCenter/**, Superwall+CustomerCenter.swift and the 11 Customer Center test files. A grep of Sources/ and Tests/ for checks at or below the floor now only finds #available(watchOS 9, *), which is above it.
  • Simplified the code those checks forced: DependencyContainer._customerCenterManager is typed as CustomerCenterManager? now, not Any? with a cast. currentStorefrontCountryCode() is a single return with no dead return nil. The two compound if #available(...), let ... conditions in ProductDisplayInfo.init keep only their optional bindings. The @available(swift, obsoleted: 1.0) on the Objective-C entry point stays, which is correct. Checks for iOS 16 and 17 are unchanged.
  • Fixed the public storeKitVersion doc: it no longer says the SDK falls back to StoreKit 1.
  • Removed tvOS: .tvOS(.v15) is out of Package.swift and tvOS is out of the changelog entry. CLAUDE.md now says tvOS isn't supported and gives the reason.

At a37ca1f, build-maccatalyst and Package-SwiftLint pass. build-visionos and run-tests were still pending when I wrote this.

ℹ️ Nitpicks

  • Sources/SuperwallKit/Documentation.docc/CustomerCenter.md:241: the Limitations section still says "Requires iOS 15.0+. On earlier versions, presentation calls are unavailable at compile time." The Customer Center APIs have no availability attributes now, and the SDK can't be built for earlier versions at all, so the second sentence is stale.
  • Sources/SuperwallKit/CustomerCenter/ViewModel/CustomerCenterDependencies.swift:282: "iOS 15 — the Customer Center's own floor" should now say the SDK's floor. The reasoning around it still holds.

Pullfrog  | View workflow run | Using Claude Opus | 𝕏

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs-required Ships a customer-facing change that needs a superwall/docs update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant