-
Notifications
You must be signed in to change notification settings - Fork 401
Update storefront apis #5455
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Update storefront apis #5455
Conversation
📸 Snapshot Test1 modified, 692 unchanged
🛸 Powered by Emerge Tools |
tonidero
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some comments and questions... But I don't think anything is blocking. Thanks for doing this!
| set { self.systemInfo.finishTransactions = newValue.finishTransactions } | ||
| } | ||
|
|
||
| @objc public var storeFrontCountryCode: String? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we deprecate this property and provide an async/callback API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about it, but wasn't really sure.
It can be done in a separate PR, with these considerations:
- There's one downside: the new API wouldn't be ObjC-compatible. We could create a ObjC-compatible method with a completion handler for it if we think it's needed.
- I can't think of a good name for it, as the most straightforward one is taken. We could "fix" the existing name by removing the capital case "F", having the new property be
storefrontCountryCode, but I think it's too similar to the one we'd be deprecating.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's a good point... I think this is something we might want to consider breaking in the next major. Should probably take note in Linear so we don't forget for the next major, whenever that is.
…ient A way deeper rewrite is needed to introduce `Task`s in `HTTPClient` without breaking the current behavior as defined by unit tests
4e4ac34 to
b85bc3f
Compare
b85bc3f to
449f4f0
Compare
| /// Returns the current storefront synchronously. | ||
| /// | ||
| /// Because this method is synchronous, it should be used only from a background thread to avoid blocking | ||
| /// the main thread. | ||
| private func getStorefrontSynchronously() -> StorefrontType? { | ||
| let storefront: Atomic<StorefrontType?> = .init(nil) | ||
| let semaphore = DispatchSemaphore(value: 0) | ||
|
|
||
| Task { | ||
| storefront.value = await self.systemInfo.storefront | ||
| semaphore.signal() | ||
| } | ||
|
|
||
| semaphore.wait() | ||
|
|
||
| return storefront.value | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying to introduce an async call in HTTPClient got more complicated than I expected. Introducing a Task to asynchronous get the Storefront for the defaultHeaders in this call
purchases-ios/Sources/Networking/HTTPClient/HTTPClient.swift
Lines 99 to 104 in 85c8534
| self.perform(request: .init(httpRequest: request, | |
| authHeaders: self.authHeaders, | |
| defaultHeaders: self.defaultHeaders, | |
| verificationMode: verificationMode ?? self.systemInfo.responseVerificationMode, | |
| internalSettings: self.systemInfo.dangerousSettings.internalSettings, | |
| completionHandler: completionHandler)) |
cannot be done without breaking the expectation of requests happening serially, as tested by
| func testPerformSerialRequestWaitsUntilRequestsAreDoneBeforeStartingNext() { |
A deeper migration of HTTPClient to support async/await tasks would be needed, I think.
For now, I just went with this workaround to keep the storefront getter synchronous by using a Semaphore. Note that while this would block the current thread, this is OK as long as this blocking never happens in the main thread, which is true since all network operations are added to a background queue. See
purchases-ios/Sources/Networking/Backend.swift
Lines 48 to 54 in 85c8534
| let config = BackendConfiguration(httpClient: httpClient, | |
| operationDispatcher: operationDispatcher, | |
| operationQueue: QueueProvider.createBackendQueue(), | |
| diagnosticsQueue: QueueProvider.createDiagnosticsQueue(), | |
| systemInfo: systemInfo, | |
| offlineCustomerInfoCreator: offlineCustomerInfoCreator, | |
| dateProvider: dateProvider) |
The (only) advantage here is that we are now using SK2's storefront when available.
The alternative would be keep using the existing (but deprecated since iOS 26) SK1's storefront getter which is synchronous but also blocking as per its documentation. The advantage is that we wouldn't need this workaround.
@RevenueCat/coresdk please, let me know what you think!
|
@RCGitBot please test |
Motivation
As stated in SK1's
SKPaymentQueue'sstorefrontproperty documentation:The iOS SDK is currently using this API as a synchronous API and not considering that it may be a blocking call.
In addition, this API has been deprecated in iOS 26.
Description
The main changes in this PR is to make
SystemInfo'sstorefrontan async getter. Under the hood:Storefront'scurrentasync property by default in iOS 15+.SKPaymentQueue'sstorefront, but converted into an async operation withTask.detached, to avoid blocking (as the documentation recommends).Most of the changes are a consequence of making it async: e.g.
HTTPClient, diagnostics tracking methods that receive the storefront and unit tests.