Skip to content

Commit

Permalink
[IAP][ios] no more extra request on purchaseItemAsync (expo#13961)
Browse files Browse the repository at this point in the history
* [IAP][ios] cache products; no need to make extra request on purchaseItemAsync

* changelog

* Update packages/expo-in-app-purchases/CHANGELOG.md

Co-authored-by: Expo CI <34669131+expo-ci@users.noreply.github.com>

* Update packages/expo-in-app-purchases/ios/EXInAppPurchases/EXInAppPurchasesModule.m

Co-authored-by: James Ide <ide@users.noreply.github.com>

Co-authored-by: Expo CI <34669131+expo-ci@users.noreply.github.com>
Co-authored-by: James Ide <ide@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 13, 2021
1 parent 0eba1ec commit d05b4e8
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 52 deletions.
1 change: 1 addition & 0 deletions packages/expo-in-app-purchases/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
### 💡 Others

- Updated Google Play Billing from v2 to v4. ([#13884](https://github.com/expo/expo/pull/13884) by [@cruzach](https://github.com/cruzach))
- Cache products on iOS when calling `getProductsAsync`, so that `purchaseItemAsync` no longer needs to make a second request to StoreKit. This matches the Android implementation. ([#13961](https://github.com/expo/expo/pull/13961) by [@cruzach](https://github.com/cruzach))

## 10.2.0 — 2021-06-16

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
@interface EXInAppPurchasesModule ()

@property (weak, nonatomic) UMModuleRegistry *moduleRegistry;
@property (nonatomic, assign) BOOL queryingItems;
@property (nonatomic, weak) id <UMEventEmitterService> eventEmitter;
@property (strong, nonatomic) NSMutableDictionary *promises;
@property (strong, nonatomic) NSMutableDictionary *pendingTransactions;
@property (strong, nonatomic) NSMutableSet *retrievedItems;
@property (strong, nonatomic) NSMutableSet<SKProduct *> *cachedProducts;
@property (strong, nonatomic) SKProductsRequest *request;

@end
Expand Down Expand Up @@ -53,9 +52,8 @@ - (void)stopObserving {}

_promises = [NSMutableDictionary dictionary];
_pendingTransactions = [NSMutableDictionary dictionary];
_retrievedItems = [NSMutableSet set];
_cachedProducts = [NSMutableSet set];

_queryingItems = NO;
resolve(nil);
}

Expand All @@ -65,16 +63,11 @@ - (void)stopObserving {}
reject:(UMPromiseRejectBlock)reject)
{
[self setPromise:kEXQueryPurchasableKey resolve:resolve reject:reject];

for (NSString *identifier in productIDs) {
[_retrievedItems addObject:identifier];
}
_queryingItems = YES;
[self requestProducts:productIDs];
}

UM_EXPORT_METHOD_AS(purchaseItemAsync,
purchaseItemAsync:(NSString *)productIdentifier
purchaseItemAsync:(NSString *)productIdToPurchase
replace:(NSString *)oldItem // ignore on iOS
resolve:(UMPromiseResolveBlock)resolve
reject:(UMPromiseRejectBlock)reject)
Expand All @@ -83,17 +76,19 @@ - (void)stopObserving {}
reject(@"E_MISSING_PERMISSIONS", @"User cannot make payments", nil);
return;
}
if (![_retrievedItems containsObject:productIdentifier]) {
reject(@"E_ITEM_NOT_QUERIED", @"Must query item from store before calling purchase", nil);
return;

for (SKProduct *product in _cachedProducts) {
if ([product.productIdentifier isEqualToString:productIdToPurchase]) {
// Make the request
BOOL promiseSet = [self setPromise:productIdToPurchase resolve:resolve reject:reject];
if (promiseSet) {
[self purchase:product];
}
return;
}
}

// Make the request
BOOL promiseSet = [self setPromise:productIdentifier resolve:resolve reject:reject];
if (promiseSet) {
_queryingItems = NO;
[self requestProducts:@[productIdentifier]];
}
reject(@"E_ITEM_NOT_QUERIED", @"Must query item from store before calling purchase", nil);
}

UM_EXPORT_METHOD_AS(finishTransactionAsync,
Expand Down Expand Up @@ -145,32 +140,6 @@ - (void)requestProducts:(NSArray *)productIdentifiers
[productsRequest start];
}

- (void)handleQuery:(SKProductsResponse *)response {
NSMutableArray *result = [NSMutableArray array];

for (SKProduct *validProduct in response.products) {
if (!validProduct.localizedDescription) { continue; } // skip product with nil values - this can happen if it is in review "rejected" state
NSDictionary *productData = [self getProductData:validProduct];
[result addObject:productData];
}

_queryingItems = NO;
NSDictionary *res = [self formatResults:result withResponseCode:OK];
[self resolvePromise:kEXQueryPurchasableKey value:res];
}

-(void)handlePurchase:(SKProductsResponse *)response {
for (NSString *invalidIdentifier in response.invalidProductIdentifiers) {
NSDictionary *results = [self formatResults:SKErrorStoreProductNotAvailable];
[self resolvePromise:invalidIdentifier value:results];
}

for (SKProduct *validProduct in response.products) {
if (!validProduct.localizedDescription) { continue; } // skip product with nil values - this can happen if it is in review "rejected" state
[self purchase:validProduct];
}
}

- (void)purchase:(SKProduct *)product
{
SKPayment *payment = [SKPayment paymentWithProduct:product];
Expand All @@ -179,16 +148,20 @@ - (void)purchase:(SKProduct *)product

# pragma mark - StoreKit Transaction Observer Methods

/*
This function is called both when purchasing an item and querying for item data
*/
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
if (_queryingItems) {
[self handleQuery:response];
} else {
[self handlePurchase:response];
NSMutableArray *result = [NSMutableArray array];
[_cachedProducts removeAllObjects];

for (SKProduct *validProduct in response.products) {
if (!validProduct.localizedDescription) { continue; } // skip product with nil values - this can happen if it is in review "rejected" state
[_cachedProducts addObject:validProduct];
NSDictionary *productData = [self getProductData:validProduct];
[result addObject:productData];
}

NSDictionary *res = [self formatResults:result withResponseCode:OK];
[self resolvePromise:kEXQueryPurchasableKey value:res];
}

- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
Expand Down

0 comments on commit d05b4e8

Please sign in to comment.