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
4 changes: 2 additions & 2 deletions platforms/swift/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ ShopifyCheckoutKit.present(
)
```

Preloading is a best-effort performance hint, not a guarantee. If the preload is unavailable, incomplete, or for a different checkout URL, checkout loads normally during presentation. A preloaded checkout reflects the cart represented by the URL passed to `preload`, so call `preload` again after cart changes produce a new checkout URL.
Preloading is a best-effort performance hint, not a guarantee. If the preload is unavailable, incomplete, or for a different checkout URL, checkout loads normally during presentation. A preloaded checkout reflects the cart represented by the URL passed to `preload`, so call `preload` again after cart changes produce a new checkout URL. It also reflects the `appearance` in `ShopifyCheckoutKit.configuration` at the time of the `preload` call: in SwiftUI, keep any instance `.appearance(...)` modifier aligned with the global appearance when relying on preloading, since a mismatch skips and clears the preload at presentation.

Avoid preloading on every add-to-cart or cart mutation. Preload only when buyer intent is strong enough to justify the additional client and network work.

Expand Down Expand Up @@ -259,7 +259,7 @@ ShopifyCheckoutKit.configure {
}
```

`ShopifyCheckout` uses the global configuration as its defaults. When present, modifiers such as `.appearance(...)`, `.tintColor(...)`, and `.title(...)` take precedence over the corresponding `ShopifyCheckoutKit.configuration` values for that checkout.
`ShopifyCheckout` uses the global configuration as its defaults. When present, modifiers such as `.appearance(...)`, `.tintColor(...)`, and `.title(...)` take precedence over the corresponding `ShopifyCheckoutKit.configuration` values for that checkout. Applying a modifier does not mutate `ShopifyCheckoutKit.configuration`, so it does not trigger the preload invalidation described above. Whether the preload is reused is decided later, at presentation: `preload` prepares the checkout URL using the global configuration's `appearance`, so a `ShopifyCheckout` reuses the preload only when its effective appearance matches the one that was preloaded. Presenting with a different instance `.appearance(...)` loads checkout fresh and discards the preloaded checkout. `.tintColor(...)`, `.backgroundColor(...)`, `.title(...)`, and `.closeButtonTintColor(...)` do not affect the checkout URL and never affect preload reuse.

| Option | Default | Purpose |
| --- | --- | --- |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,14 @@ class CheckoutConfigurableTests: XCTestCase {
override func setUp() async throws {
try await super.setUp()
ShopifyCheckoutKit.configuration = Configuration()
CheckoutWebView.invalidate()
checkoutURL = URL(string: "https://www.shopify.com")
shopifyCheckout = ShopifyCheckout(checkout: checkoutURL)
}

override func tearDown() async throws {
ShopifyCheckoutKit.configuration = Configuration()
CheckoutWebView.invalidate()
try await super.tearDown()
}

Expand Down Expand Up @@ -171,6 +173,41 @@ class CheckoutConfigurableTests: XCTestCase {
XCTAssertTrue(CheckoutWebView.preloadCache.hasEntry())
}

func testPresentationReusesPreloadWhenInstanceAppearanceMatchesPreloadedAppearance() async {
await Task.yield()
ShopifyCheckoutKit.preload(checkout: checkoutURL)
CheckoutWebView.preloadCache.transition(to: .ready)
let preloaded = CheckoutWebView.for(checkout: CheckoutURLDecorator.decorate(checkoutURL))

let sheet = shopifyCheckout
.backgroundColor(.red)
.tintColor(.blue)
.title("Instance checkout")
.closeButtonTintColor(.green)

XCTAssertEqual(sheet.decoratedCheckoutURL, CheckoutURLDecorator.decorate(checkoutURL))
let presented = CheckoutWebView.for(checkout: sheet.decoratedCheckoutURL)

XCTAssertTrue(presented === preloaded)
XCTAssertTrue(CheckoutWebView.preloadCache.hasEntry())
}

func testPresentationWithDifferentInstanceAppearanceMissesAndEvictsPreload() async {
await Task.yield()
ShopifyCheckoutKit.preload(checkout: checkoutURL)
CheckoutWebView.preloadCache.transition(to: .ready)
XCTAssertTrue(CheckoutWebView.preloadCache.hasEntry())

let sheet = shopifyCheckout.appearance(.app(.dark))
XCTAssertNotEqual(sheet.decoratedCheckoutURL, CheckoutURLDecorator.decorate(checkoutURL))

let fresh = CheckoutWebView.for(checkout: sheet.decoratedCheckoutURL)

XCTAssertNil(fresh.url)
XCTAssertFalse(CheckoutWebView.preloadCache.hasEntry())
XCTAssertFalse(CheckoutWebView.preloadCache.hasActiveKeepAlive())
}

func testModifiersApplyToAnyConformerWithoutCasts() {
let fixture = ConfigurableFixture()
.backgroundColor(.red)
Expand Down
Loading