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.
applicationUsername
in addPayment
and restoreTransactions
And allow you to choose how to store purchases (e.g., keychain).