Skip to content

Purchases initialization: refactor to avoid multiple concurrent instances in memory#2180

Merged
NachoSoto merged 2 commits into
mainfrom
purchases-init-concurrent
Dec 21, 2022
Merged

Purchases initialization: refactor to avoid multiple concurrent instances in memory#2180
NachoSoto merged 2 commits into
mainfrom
purchases-init-concurrent

Conversation

@NachoSoto

@NachoSoto NachoSoto commented Dec 20, 2022

Copy link
Copy Markdown
Contributor

We allow calling Purchases.configure multiple times. Likely a programmer error, but it's common in tests, or for example in Purchase Tester where users can re-configure the SDK at runtime (#1999), which is where I saw this issue.

When this happens, the SDK was creating the new instance before clearing the old one from memory (clear thanks to #2082). Notice how the first deinit happens after the second init:

[Purchases] - VERBOSE: Purchases.init: created new Purchases instance: Purchases (0x0000600003ca4200)
[Purchases] - INFO: ℹ️ Purchases instance already set. Did you mean to configure two Purchases objects?
[Purchases] - VERBOSE: Purchases.init: created new Purchases instance: Purchases (0x0000600003c8a100)
[Purchases] - VERBOSE: Purchases.deinit: Purchases (0x0000600003ca4200)
[Purchases] - VERBOSE: Purchases.deinit: Purchases (0x0000600003c8a100)

With this change, we avoid that, which could also avoid issues with concurrent requests (CSDK-517):

[Purchases] - VERBOSE: Purchases.init: created new Purchases instance: Purchases (0x0000600002b6e600)
[Purchases] - INFO: ℹ️ Purchases instance already set. Did you mean to configure two Purchases objects?
[Purchases] - VERBOSE: Purchases.deinit: Purchases (0x0000600002b6e600)
[Purchases] - VERBOSE: Purchases.init: created new Purchases instance: Purchases (0x0000600002b68100)
[Purchases] - VERBOSE: Purchases.deinit: Purchases (0x0000600002b68100)

Unfortunately we don't have an easy way to test this, especially considering that we prohibit multiple instances in tests (#2100).

…tances in memory

We allow calling `Purchases.configure` multiple times. Likely a programmer error, but it's common in tests, or for example in `Purchase Tester` where users can re-configure the SDK at runtime (#1999).
When this happens, the SDK was creating the new instance before clearing the old one from memory (clear thanks to #2082, notice how the first `deinit` happens after the second `init`):
```
[Purchases] - VERBOSE: Purchases.init: created new Purchases instance: Purchases (0x0000600003ca4200)
[Purchases] - INFO: ℹ️ Purchases instance already set. Did you mean to configure two Purchases objects?
[Purchases] - VERBOSE: Purchases.init: created new Purchases instance: Purchases (0x0000600003c8a100)
[Purchases] - VERBOSE: Purchases.deinit: Purchases (0x0000600003ca4200)
[Purchases] - VERBOSE: Purchases.deinit: Purchases (0x0000600003c8a100)
```

With this change, we avoid that, which could also avoid issues with concurrent requests ([CSDK-517]):
```
[Purchases] - VERBOSE: Purchases.init: created new Purchases instance: Purchases (0x0000600002b6e600)
[Purchases] - INFO: ℹ️ Purchases instance already set. Did you mean to configure two Purchases objects?
[Purchases] - VERBOSE: Purchases.deinit: Purchases (0x0000600002b6e600)
[Purchases] - VERBOSE: Purchases.init: created new Purchases instance: Purchases (0x0000600002b68100)
[Purchases] - VERBOSE: Purchases.deinit: Purchases (0x0000600002b68100)
```
@NachoSoto NachoSoto added the perf label Dec 20, 2022
@NachoSoto NachoSoto requested a review from a team December 20, 2022 21:59
static func setDefaultInstance(_ purchases: Purchases) {
self.purchases.modify { currentInstance in
@discardableResult
static func setDefaultInstance(_ purchases: @autoclosure () -> Purchases) -> Purchases {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do you mind adding a comment here why this is an autoclosure and not just an instance of Purchases?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good idea

@NachoSoto NachoSoto merged commit 3bcda56 into main Dec 21, 2022
@NachoSoto NachoSoto deleted the purchases-init-concurrent branch December 21, 2022 16:22
NachoSoto pushed a commit that referenced this pull request Dec 21, 2022
**This is an automatic release.**

### Bugfixes
* `ErrorUtils.purchasesError(withUntypedError:)`: handle `PublicError`s
(#2165) via NachoSoto (@NachoSoto)
* Fixed race condition finishing `SK1` transactions (#2148) via
NachoSoto (@NachoSoto)
* `IntroEligibilityStatus`: added `CustomStringConvertible`
implementation (#2182) via NachoSoto (@NachoSoto)
* `BundleSandboxEnvironmentDetector`: fixed logic for `macOS` (#2176)
via NachoSoto (@NachoSoto)
* Fixed `AttributionFetcher.adServicesToken` hanging when called in
simulator (#2157) via NachoSoto (@NachoSoto)
### Other Changes
* `Purchase Tester`: added ability to purchase products directly with
`StoreKit` (#2172) via NachoSoto (@NachoSoto)
* `DNSChecker`: simplified `NetworkError` initialization (#2166) via
NachoSoto (@NachoSoto)
* `Purchases` initialization: refactor to avoid multiple concurrent
instances in memory (#2180) via NachoSoto (@NachoSoto)
* `Purchase Tester`: added button to clear messages on logger view
(#2179) via NachoSoto (@NachoSoto)
* `NetworkOperation`: added assertion to ensure that subclasses call
completion (#2138) via NachoSoto (@NachoSoto)
* `CacheableNetworkOperation`: avoid unnecessarily creating operations
for cache hits (#2135) via NachoSoto (@NachoSoto)
* `PurchaseTester`: fixed `macOS` support (#2175) via NachoSoto
(@NachoSoto)
* `IntroEligibilityCalculator`: added log including `AppleReceipt`
(#2181) via NachoSoto (@NachoSoto)
* `Purchase Tester`: fixed scene manifest (#2170) via NachoSoto
(@NachoSoto)
* `HTTPClientTests`: refactored to use `waitUntil` (#2168) via NachoSoto
(@NachoSoto)
* `Integration Tests`: split up tests in smaller files (#2158) via
NachoSoto (@NachoSoto)
* `StoreKitRequestFetcher`: removed unnecessary dispatch (#2152) via
NachoSoto (@NachoSoto)
* `Purchase Tester`: added companion `watchOS` app (#2140) via NachoSoto
(@NachoSoto)
* `StoreKit1Wrapper`: added warning if receiving too many updated
transactions (#2117) via NachoSoto (@NachoSoto)
* `StoreKitTestHelpers`: cleaned up unnecessary log (#2177) via
NachoSoto (@NachoSoto)
* `TrialOrIntroPriceEligibilityCheckerSK1Tests`: use `waitUntilValue`
(#2173) via NachoSoto (@NachoSoto)
* `DNSChecker`: added log with resolved host (#2167) via NachoSoto
(@NachoSoto)
* `MagicWeatherSwiftUI`: updated `README` to point to workspace (#2142)
via NachoSoto (@NachoSoto)
* `Purchase Tester`: fixed `.storekit` config file reference (#2171) via
NachoSoto (@NachoSoto)
* `Purchase Tester`: fixed error alerts (#2169) via NachoSoto
(@NachoSoto)
* `CI`: don't make releases until `release-checks` pass (#2162) via
NachoSoto (@NachoSoto)
* `Fastfile`: changed `match` to `readonly` (#2161) via NachoSoto
(@NachoSoto)
@vegaro vegaro added pr:other and removed pr:perf labels Sep 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants