Skip to content

Commit 20c857c

Browse files
authored
Avoid StoreKit product lookups during iOS startup (#8849)
* Add resumePendingPurchasesIfNeeded * Address StoreKit review feedback
1 parent 7557454 commit 20c857c

2 files changed

Lines changed: 19 additions & 19 deletions

File tree

lib/core/services/app_purchase.dart

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ class AppPurchase {
5454
// emit an empty stream event the way Play Billing does).
5555
bool _restoreReceivedAny = false;
5656

57+
/// Starts listening for store purchase updates without fetching product
58+
/// details. Product lookup stays user-initiated to keep StoreKit out of the
59+
/// normal app launch path on iOS.
5760
void init() {
5861
if (!_canInitializeStorePurchases()) {
5962
return;
@@ -68,11 +71,6 @@ class AppPurchase {
6871
onDone: _updateStreamOnDone,
6972
onError: _updateStreamOnError,
7073
);
71-
unawaited(
72-
fetchSubscriptions().catchError((Object e, StackTrace st) {
73-
appLogger.error('[AppPurchase] init: fetchSubscriptions failed', e, st);
74-
}),
75-
);
7674
}
7775

7876
bool _canInitializeStorePurchases() {
@@ -100,31 +98,31 @@ class AppPurchase {
10098
return allowed;
10199
}
102100

103-
Future<bool> _initPlayBillingIfAllowed() async {
101+
Future<bool> _ensurePurchaseStreamReady() async {
104102
appLogger.info(
105-
'[AppPurchase] _initPlayBillingIfAllowed: '
103+
'[AppPurchase] _ensurePurchaseStreamReady: '
106104
'country=${CountryCode.current}, isKnown=${CountryCode.isKnown}, '
107105
'subscribed=${_subscription != null}',
108106
);
109107
init();
110108
if (_subscription != null) {
111-
appLogger.info('[AppPurchase] _initPlayBillingIfAllowed: ready');
109+
appLogger.info('[AppPurchase] _ensurePurchaseStreamReady: ready');
112110
return true;
113111
}
114112
if (Platform.isAndroid && !CountryCode.isKnown) {
115113
appLogger.info(
116-
'[AppPurchase] _initPlayBillingIfAllowed: country unknown, '
114+
'[AppPurchase] _ensurePurchaseStreamReady: country unknown, '
117115
'waiting for country-code event…',
118116
);
119117
final known = await CountryCode.waitUntilKnown();
120118
appLogger.info(
121-
'[AppPurchase] _initPlayBillingIfAllowed: waitUntilKnown returned '
119+
'[AppPurchase] _ensurePurchaseStreamReady: waitUntilKnown returned '
122120
'$known (country=${CountryCode.current}); retrying init',
123121
);
124122
init();
125123
}
126124
final ready = _subscription != null;
127-
appLogger.info('[AppPurchase] _initPlayBillingIfAllowed: ready=$ready');
125+
appLogger.info('[AppPurchase] _ensurePurchaseStreamReady: ready=$ready');
128126
return ready;
129127
}
130128

@@ -189,7 +187,7 @@ class AppPurchase {
189187
}
190188

191189
final error = StateError(
192-
'Unable to load App Store products after $maxAttempts attempts',
190+
'Unable to load in-app purchase products after $maxAttempts attempts',
193191
);
194192
// Safely complete the completer with an error, if it is still pending.
195193
if (_productsLoadedCompleter != null &&
@@ -230,9 +228,9 @@ class AppPurchase {
230228
// Store the exact plan id user chose (ex: "1y-usd-10")
231229
_pendingPlanId = plan;
232230

233-
if (!await _initPlayBillingIfAllowed()) {
231+
if (!await _ensurePurchaseStreamReady()) {
234232
_onError?.call(
235-
"Unable to load App Store products. Check your network and try again.",
233+
"Unable to access in-app purchases. Check your network and try again.",
236234
);
237235
return;
238236
}
@@ -241,7 +239,7 @@ class AppPurchase {
241239
await _waitForProducts();
242240
} catch (_) {
243241
_onError?.call(
244-
"Unable to load App Store products. Check your network and try again.",
242+
"Unable to load in-app purchase products. Check your network and try again.",
245243
);
246244
return;
247245
}
@@ -297,12 +295,12 @@ class AppPurchase {
297295
_restoreReceivedAny = false;
298296
_pendingPlanId = null;
299297

300-
if (!await _initPlayBillingIfAllowed()) {
298+
if (!await _ensurePurchaseStreamReady()) {
301299
_isRestoreFlow = false;
302300
final onError = _onError;
303301
clearCallbacks();
304302
onError?.call(
305-
"Unable to load App Store products. Check your network and try again.",
303+
"Unable to access in-app purchases. Check your network and try again.",
306304
);
307305
return;
308306
}

lib/core/services/injection_container.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,12 @@ Future<void> injectServices() async {
4444
);
4545
sl.registerSingleton<LanternPlatformService>(LanternPlatformService());
4646

47-
appLogger.debug('Initializing AppPurchase...');
47+
appLogger.debug('Registering AppPurchase...');
4848
final appPurchase = AppPurchase();
4949
sl.registerSingleton<AppPurchase>(appPurchase);
50-
if (!PlatformUtils.isAndroid) {
50+
if (PlatformUtils.isIOS) {
51+
// StoreKit can deliver pending transaction updates at launch; init()
52+
// only subscribes to the stream and does not fetch product details.
5153
appPurchase.init();
5254
}
5355
appLogger.debug('AppPurchase registered');

0 commit comments

Comments
 (0)