Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .circleci/default_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,40 @@ jobs:
- slack-notify-on-fail
- github-notify-pr-on-fail

# As of May 2026, StoreKit tests fail to fetch products on iOS simulators when
# building with Xcode 26.4+ (see run-test-ios-26 / build-xcode-265 notes below).
# The same bug does not appear to reproduce on Mac Catalyst, so this job runs the
# full iOS test plan (including StoreKit tests) on the Mac Catalyst variant with
# Xcode 26.5 to confirm this on CI.
run-test-mac-catalyst-xcode-26:
executor:
name: macos-executor
xcode_version: "26.5"

steps:
- checkout
- install-dependencies

# === Mac Catalyst tests ===
- run:
name: Run Mac Catalyst tests
command: bundle exec fastlane mac test_catalyst
no_output_timeout: 15m
- compress_result_bundle:
directory: fastlane/test_output/xctest/catalyst
bundle_name: RevenueCat
- create-snapshot-pr-if-needed:
condition: << pipeline.parameters.generate_snapshots >>
job: "create_snapshot_pr"
version: "mac-catalyst-26"
- store_test_results:
path: fastlane/test_output
- store_artifacts:
path: fastlane/test_output/xctest
destination: scan-test-output-mac-catalyst
- slack-notify-on-fail
- github-notify-pr-on-fail

# As of May 12, 2026, there is a StoreKit bug that is preventing us from running unit tests
# when building with Xcode 26.4+. When built with Xcode 26.4+, tests that fetch products
# with StoreKit with a SKConfig file fail to fetch products. Thus, a good chunk of our
Expand Down Expand Up @@ -2157,6 +2191,7 @@ workflows:
- run-test-ios-15-and-14
- run-test-tvos-and-macos
- run-test-watchos
- run-test-mac-catalyst-xcode-26
- backend-integration-tests-SK2 # Snapshots from this job are shared with all BackendIntegrationTests


Expand Down Expand Up @@ -2217,6 +2252,9 @@ workflows:
- build-xcode-265:
context:
- slack-secrets
- run-test-mac-catalyst-xcode-26:
context:
- slack-secrets
- pod-lib-lint:
context:
- slack-secrets
Expand Down Expand Up @@ -2381,6 +2419,7 @@ workflows:
- check-api-changes-revenuecatui
- run-test-ios-26
- build-xcode-265
- run-test-mac-catalyst-xcode-26
- pod-lib-lint
- run-revenuecat-ui-ios-26
- emerge_purchases_ui_snapshot_tests
Expand Down Expand Up @@ -2685,6 +2724,9 @@ workflows:
- build-xcode-265:
context:
- slack-secrets
- run-test-mac-catalyst-xcode-26:
context:
- slack-secrets
- pod-lib-lint:
context:
- slack-secrets
Expand All @@ -2706,6 +2748,7 @@ workflows:
- lint
- run-test-ios-26
- build-xcode-265
- run-test-mac-catalyst-xcode-26
- pod-lib-lint
- run-revenuecat-ui-ios-26
- emerge_purchases_ui_snapshot_tests
Expand Down
1 change: 1 addition & 0 deletions .circleci/generate-requested-jobs-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const JOBS = {
"run-test-ios-16": ["slack-secrets"],
"run-test-ios-18-and-17": ["slack-secrets"],
"run-test-ios-26": ["slack-secrets"],
"run-test-mac-catalyst-xcode-26": ["slack-secrets"],
"run-test-tvos-and-macos": ["slack-secrets"],
"run-test-watchos": ["slack-secrets"],
"spm-receipt-parser": ["slack-secrets"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class LocalReceiptParserStoreKitTests: StoreKitConfigTestCase {
let receipt = try self.parser.parse(from: data)

expect(receipt.bundleId) == "com.revenuecat.StoreKitUnitTestsHostApp"
#if os(macOS)
#if os(macOS) || targetEnvironment(macCatalyst)
expect(receipt.applicationVersion) == SystemInfo.frameworkVersion.replacingOccurrences(
of: "-SNAPSHOT",
with: ""
Expand Down
5 changes: 4 additions & 1 deletion Tests/StoreKitUnitTests/ProductsManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,10 @@ class SK2ProductsManagerDiagnosticsTrackingTests: ProductsManagerTests {
expect(params.storeKitErrorDescription).to(beNil())
}

#if swift(>=5.9)
// `SKTestSession.setSimulatedError(_:forAPI:)` is part of the StoreKitTest Swift overlay,
// which ships no Mac Catalyst (macabi) slice in Xcode 26.5, so it can't be compiled for Catalyst
// (Apple bug FB22922982).
#if swift(>=5.9) && !targetEnvironment(macCatalyst)
@available(iOS 17.0, tvOS 17.0, macOS 14.0, watchOS 10.0, *)
func testFetchProductsWithIdentifiersSK2ErrorTracksCorrectly() async throws {
try AvailabilityChecks.iOS17APIAvailableOrSkipTest()
Expand Down
20 changes: 18 additions & 2 deletions Tests/StoreKitUnitTests/PurchasesOrchestratorSK2Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@ class PurchasesOrchestratorSK2Tests: BasePurchasesOrchestratorTests, PurchasesOr
expect(self.backend.invokedPostReceiptData) == false
}

#if swift(>=5.9)
// `SKTestSession.setSimulatedError(_:forAPI:)` is part of the StoreKitTest Swift overlay,
// which ships no Mac Catalyst (macabi) slice in Xcode 26.5, so it can't be compiled for Catalyst
// (Apple bug FB22922982).
#if swift(>=5.9) && !targetEnvironment(macCatalyst)
@available(iOS 17.0, tvOS 17.0, watchOS 10.0, macOS 14.0, *)
func testPurchaseSK2CancelledWithSimulatedError() async throws {
try AvailabilityChecks.iOS17APIAvailableOrSkipTest()
Expand Down Expand Up @@ -536,6 +539,15 @@ class PurchasesOrchestratorSK2Tests: BasePurchasesOrchestratorTests, PurchasesOr
}

func testPurchaseFailureClearsPresentedPaywall() async throws {
// `SKTestSession.setSimulatedError(_:forAPI:)` is part of the StoreKitTest Swift overlay,
// which ships no Mac Catalyst (macabi) slice in Xcode 26.5, so it can't be compiled for Catalyst
// (Apple bug FB22922982).
// This test relies on that fault-injection API, so it's skipped on Catalyst. The method is kept
// defined to satisfy the `PurchasesOrchestratorTests` protocol conformance.
#if targetEnvironment(macCatalyst)
throw XCTSkip("SKTestSession.setSimulatedError(_:forAPI:) is unavailable when compiling for Mac Catalyst " +
"(Apple bug FB22922982)")
#else
try AvailabilityChecks.iOS17APIAvailableOrSkipTest()
#if compiler(>=5.9)
if #available(iOS 17.0, tvOS 17.0, macOS 14.0, watchOS 10.0, *) {
Expand Down Expand Up @@ -594,6 +606,7 @@ class PurchasesOrchestratorSK2Tests: BasePurchasesOrchestratorTests, PurchasesOr
expect(
self.backend.invokedPostReceiptDataParameters?.transactionData.presentedPaywall
).to(beNil())
#endif
}

func testCancelEventClearsPresentedPaywall() async throws {
Expand Down Expand Up @@ -2501,7 +2514,10 @@ class PurchasesOrchestratorSK2Tests: BasePurchasesOrchestratorTests, PurchasesOr
}
}

#if swift(>=5.9)
// `SKTestSession.setSimulatedError(_:forAPI:)` is part of the StoreKitTest Swift overlay,
// which ships no Mac Catalyst (macabi) slice in Xcode 26.5, so it can't be compiled for Catalyst
// (Apple bug FB22922982).
#if swift(>=5.9) && !targetEnvironment(macCatalyst)
@available(iOS 17.0, tvOS 17.0, macOS 14.0, watchOS 10.0, *)
func testPurchaseWithSimulatedErrorTracksError() async throws {
try AvailabilityChecks.iOS17APIAvailableOrSkipTest()
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions Tests/UnitTests/Misc/SandboxEnvironmentDetectorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import XCTest

@testable import RevenueCat

#if !os(macOS)
// Mac Catalyst detects sandbox the same way as macOS (via the receipt environment, not the
// receipt URL path), so it uses the macOS test branch below.
#if !os(macOS) && !targetEnvironment(macCatalyst)

class SandboxEnvironmentDetectorTests: TestCase {

Expand All @@ -42,7 +44,7 @@ class SandboxEnvironmentDetectorTests: TestCase {

#else

// `macOS` sandbox detection does not rely on receipt path
// `macOS` and Mac Catalyst sandbox detection does not rely on receipt path
class SandboxEnvironmentDetectorTests: TestCase {

func testIsNotSandboxIfReceiptIsProduction() throws {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"a" : 1,
"b" : {
"b1" : false,
"b2" : {
"b21" : 1,
"b22" : "https:\/\/google.com"
},
"b3" : [
20.2,
19.99,
5
],
"b4" : 50000
},
"c" : "3",
"d" : null
}
2 changes: 1 addition & 1 deletion Tests/UnitTests/Mocks/MockPurchases.swift
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ extension MockPurchases: PurchasesType {
self.unimplemented()
}

#if os(iOS) || VISION_OS
#if (os(iOS) && !targetEnvironment(macCatalyst)) || VISION_OS

func presentCodeRedemptionSheet() {
self.unimplemented()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"headers" : {
"Authorization" : "Bearer asharedsecret",
"X-Apple-Device-Identifier" : "5D7C0074-07E4-4564-AAA4-4008D0640881",
"X-Client-Build-Version" : "12345",
"X-Client-Bundle-ID" : "com.apple.dt.xctest.tool",
"X-Client-Version" : "17.0.0",
"X-Installation-Method" : "unknown",
"X-Is-Backgrounded" : "false",
"X-Is-Debug-Build" : "true",
"X-Is-Sandbox" : "false",
"X-Observer-Mode-Enabled" : "false",
"X-Platform" : "iOS",
"X-Platform-Device" : "arm64",
"X-Platform-Flavor" : "native",
"X-Platform-Version" : "Version 17.0.0 (Build 21A342)",
"X-Preferred-Locales" : "en_EN",
"X-Retry-Count" : "0",
"X-StoreKit-Version" : "2",
"X-StoreKit2-Enabled" : "true",
"X-Storefront" : "USA",
"X-Version" : "4.0.0",
"content-type" : "application/json"
},
"request" : {
"body" : null,
"method" : "GET",
"url" : "https://api.revenuecat.com/v1/customercenter/user"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"headers" : {
"Authorization" : "Bearer asharedsecret",
"X-Apple-Device-Identifier" : "5D7C0074-07E4-4564-AAA4-4008D0640881",
"X-Client-Build-Version" : "12345",
"X-Client-Bundle-ID" : "com.apple.dt.xctest.tool",
"X-Client-Version" : "17.0.0",
"X-Installation-Method" : "unknown",
"X-Is-Backgrounded" : "false",
"X-Is-Debug-Build" : "true",
"X-Is-Sandbox" : "false",
"X-Observer-Mode-Enabled" : "false",
"X-Platform" : "iOS",
"X-Platform-Device" : "arm64",
"X-Platform-Flavor" : "native",
"X-Platform-Version" : "Version 17.0.0 (Build 21A342)",
"X-Preferred-Locales" : "en_EN",
"X-Retry-Count" : "0",
"X-StoreKit-Version" : "2",
"X-StoreKit2-Enabled" : "true",
"X-Storefront" : "USA",
"X-Version" : "4.0.0",
"content-type" : "application/json"
},
"request" : {
"body" : null,
"method" : "GET",
"url" : "https://api.revenuecat.com/v1/customercenter/user"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"headers" : {
"Authorization" : "Bearer asharedsecret",
"X-Apple-Device-Identifier" : "5D7C0074-07E4-4564-AAA4-4008D0640881",
"X-Client-Build-Version" : "12345",
"X-Client-Bundle-ID" : "com.apple.dt.xctest.tool",
"X-Client-Version" : "17.0.0",
"X-Installation-Method" : "unknown",
"X-Is-Backgrounded" : "false",
"X-Is-Debug-Build" : "true",
"X-Is-Sandbox" : "false",
"X-Observer-Mode-Enabled" : "false",
"X-Platform" : "iOS",
"X-Platform-Device" : "arm64",
"X-Platform-Flavor" : "native",
"X-Platform-Version" : "Version 17.0.0 (Build 21A342)",
"X-Preferred-Locales" : "en_EN",
"X-Retry-Count" : "0",
"X-StoreKit-Version" : "2",
"X-StoreKit2-Enabled" : "true",
"X-Storefront" : "USA",
"X-Version" : "4.0.0",
"content-type" : "application/json"
},
"request" : {
"body" : null,
"method" : "GET",
"url" : "https://api.revenuecat.com/v1/customercenter/user"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"headers" : {
"Authorization" : "Bearer asharedsecret",
"X-Apple-Device-Identifier" : "5D7C0074-07E4-4564-AAA4-4008D0640881",
"X-Client-Build-Version" : "12345",
"X-Client-Bundle-ID" : "com.apple.dt.xctest.tool",
"X-Client-Version" : "17.0.0",
"X-Installation-Method" : "unknown",
"X-Is-Backgrounded" : "false",
"X-Is-Debug-Build" : "true",
"X-Is-Sandbox" : "false",
"X-Observer-Mode-Enabled" : "false",
"X-Platform" : "iOS",
"X-Platform-Device" : "arm64",
"X-Platform-Flavor" : "native",
"X-Platform-Version" : "Version 17.0.0 (Build 21A342)",
"X-Preferred-Locales" : "en_EN",
"X-Retry-Count" : "0",
"X-StoreKit-Version" : "2",
"X-StoreKit2-Enabled" : "true",
"X-Storefront" : "USA",
"X-Version" : "4.0.0",
"content-type" : "application/json"
},
"request" : {
"body" : null,
"method" : "GET",
"url" : "https://api.revenuecat.com/v1/customercenter/user"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"headers" : {
"Authorization" : "Bearer asharedsecret",
"X-Apple-Device-Identifier" : "5D7C0074-07E4-4564-AAA4-4008D0640881",
"X-Client-Build-Version" : "12345",
"X-Client-Bundle-ID" : "com.apple.dt.xctest.tool",
"X-Client-Version" : "17.0.0",
"X-Installation-Method" : "unknown",
"X-Is-Backgrounded" : "false",
"X-Is-Debug-Build" : "true",
"X-Is-Sandbox" : "false",
"X-Observer-Mode-Enabled" : "false",
"X-Platform" : "iOS",
"X-Platform-Device" : "arm64",
"X-Platform-Flavor" : "native",
"X-Platform-Version" : "Version 17.0.0 (Build 21A342)",
"X-Preferred-Locales" : "en_EN",
"X-Retry-Count" : "0",
"X-StoreKit-Version" : "2",
"X-StoreKit2-Enabled" : "true",
"X-Storefront" : "USA",
"X-Version" : "4.0.0",
"content-type" : "application/json"
},
"request" : {
"body" : null,
"method" : "GET",
"url" : "https://api.revenuecat.com/v1/customercenter/user"
}
}
Loading