A single object is notified of the result of each request.
- (void)requestProducts { NSSet *products = [NSSet setWithArray:@[@"fabulousIdol", @"rootBeer", @"rubberChicken"]]; _request = [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObject:products]]; _request.delegate = self; [_request start]; } - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { NSLog(@"Products loaded", @""); _request = nil; } - (void)request:(SKRequest *)request didFailWithError:(NSError *)error { NSLog(@"Something went wrong", @""); _request = nil; }
Making your code more contextual and reducing coupling.
NSSet *products = [NSSet setWithArray:@[@"fabulousIdol", @"rootBeer", @"rubberChicken"]]; [[RMStore defaultStore] requestProducts:products success:^(NSArray *products, NSArray *invalidProductIdentifiers) { NSLog(@"Products loaded", @""); } failure:^(NSError *error) { NSLog(@"Something went wrong", @""); }];
RMStore adds blocks to all asynchronous StoreKit operations.
NSSet *products = [NSSet setWithArray:@[@"fabulousIdol", @"rootBeer", @"rubberChicken"]]; [[RMStore defaultStore] requestProducts:products success:^(NSArray *products, NSArray *invalidProductIdentifiers) { NSLog(@"Products loaded", @""); } failure:^(NSError *error) { NSLog(@"Something went wrong", @""); }];
[[RMStore defaultStore] addPayment:@"waxLips" success:^(SKPaymentTransaction *transaction) { NSLog(@"Product purchased", @""); } failure:^(SKPaymentTransaction *transaction, NSError *error) { NSLog(@"Something went wrong", @""); }];
[[RMStore defaultStore] restoreTransactionsOnSuccess:^{ NSLog(@"Transactions restored", @""); } failure:^(NSError *error) { NSLog(@"Something went wrong", @""); }];
RMStore sends notifications of StoreKit related events and extends NSNotification to provide relevant information.
To receive notifications, implement the desired methods of the RMStoreObserver protocol and add the observer to RMStore.
[[RMStore defaultStore] addStoreObserver:self]; // ... [[RMStore defaultStore] removeStoreObserver:self];
- (void)storeProductsRequestFailed:(NSNotification*)notification { NSError *error = notification.storeError; } - (void)storeProductsRequestFinished:(NSNotification*)notification { NSArray *products = notification.products; NSArray *invalidProductIdentifiers = notification.invalidProductIdentififers; }
Sent after a payment has been requested or for each restored transaction.
- (void)storePaymentTransactionFailed:(NSNotification*)notification { NSError *error = notification.storeError; NSString *productIdentifier = notification.productIdentifier; SKPaymentTransaction *transaction = notification.transaction; } - (void)storePaymentTransactionFinished:(NSNotification*)notification { NSString *productIdentifier = notification.productIdentifier; SKPaymentTransaction *transaction = notification.transaction; }
- (void)storeRestoreTransactionsFailed:(NSNotification*)notification; { NSError *error = notification.storeError; } - (void)storeRestoreTransactionsFinished:(NSNotification*)notification { }
RMStore doesn't perform receipt verification by default.
You can provide your own custom verification...
...or use the app-side verification provided by the library.
RMStore provides optional app-side receipt verification via RMStoreLocalReceiptVerificator.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { _receiptVerificator = [[RMStoreLocalReceiptVerificator alloc] init]; // Keep a reference to the verificator as the below property is weak [RMStore defaultStore].receiptVerificator = _receiptVerificator; // Your code return YES; }
Implement the RMStoreReceiptVerificator protocol:
- (void)verifyReceiptOfTransaction:(SKPaymentTransaction*)transaction success:(void (^)())successBlock failure:(void (^)(NSError *error))failureBlock;
You will also need to set the receiptVerificator delegate at startup, as indicated before.
RMStore stores transactions in NSUserDefaults with weak obfuscation. It also offers various methods to query and manage purchases.
Non-consumables can only be purchased once. To know if a non-consumable has been purchased:
BOOL purchased = [[RMStore defaultStore] isPurchasedForIdentifier:@"fabulousIdol"];
Consumables can be purchased more than once and tipically will be consumed at most once per purchase. Normally you would:
NSInteger purchaseCount = [[RMStore defaultStore] countPurchasesForIdentifier:@"banana"]; if (purchaseCount > 0) { BOOL success = [[RMStore defaultStore] consumeProductForIdentifier:@"banana"]; }
By default RMStore stores transactions in NSUserDefaults as objects using NSCoding, as a form of weak obfuscation.
Implement the RMStoreTransactionObfuscator protocol and set the transactionObfuscator delegate at startup.
- (NSData*)dataWithTransaction:(RMStoreTransaction*)transaction; - (RMStoreTransaction*)transactionWithData:(NSData*)data;
You will be obfuscating RMStoreTransaction instances, an analogue of SKPaymentTransaction which supports NSCopying, unlike the original.
RMStore is in version 0.3.
It works but its interface might change.
Ongoing work in branch ios7.
And allow you to choose how to store purchases (e.g., keychain).