NEW: Check out the CargoManager Example iOS App.
Implement StoreKit easily
CargoManager
is an open source library that helps you implement IAPs for iOS apps in a simple and encapsulated way by using the by using the delegate pattern.
StoreKit
is the Apple framework for making In-App Purchases. It's pretty good but it is very low level and has a few rough edges. CargoBay
is really nifty, but it keeps coding of In-App Purchases quite at a low level and it still is an involved process.
CargoManager
abstracts most details of StoreKit and CargoBay by providing a simple and pretty much automated manager for your In-App Purchases.
With CargoManager you only have to implement two protocols in your own classes. You will need to set these objects as CargoManager delegates:
- An object conforming to the
CargoManagerContentDelegate
protocol. This delegate is mandatory. It provides all available Product ID strings to CargoManager (which in turn caches the returned products for store population). It also should respond to successful transaction events by providing the user requested content and storing it persistently to disk. Ideally, this delegate should be a singleton and should be alive at all times (a transaction is kept as pending even after relaunching the game, so it can complete even when you are not looking at the store UI). - An object conforming to the
CargoManagerUIDelegate
protocol. This delegate is optional. It should update the UI on both transaction successful and transaction failed events (if you create a modal "processing IAP screen" you can dismiss it by using this delegate).
Please note that before attempting to use CargoManager
or CargoBay
it is strongly recommended that you fully understand how StoreKit works. The In-App Purchase Programming Guide by Apple provides a concise and gentle introduction.
In addition to the protocols, you will need to call these provided methods:
// Call this method after setting the contentDelegate
// it setups CargoManager to work with CargoBay and
// launches the initial product request.
- (void)loadStore;
// Get a product from the cached products.
- (SKProduct *)productForIdentifier:(NSString *)identifier;
// Start an In-App purchase.
- (void)buyProduct:(SKProduct *)product;
Finally, CargoManager
provides the following properties:
@property (nonatomic, readonly) BOOL productRequestDidReceiveResponse;
@property (nonatomic, readonly) BOOL productRequestError;
and also sends the CMProductRequestDidReceiveResponseNotification
notification after it caches the products. These are needed because if you enter the store quick enough after the app launches, the initial product request will not have resolved from Apple servers yet, so you won't be able to populate item prices. Instead, you can observe this notification to populate your store UI as soon as they load (you can do that even having the store UI open in your app, as to not artificially delay the user entering the store).
Last but not least, CargoManager includes a category for Apple's SKProduct, which provides the following
@property (nonatomic, readonly) NSString *localizedPrice;
which returns the product price formatted as a currency string in the user locale.
// Your AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Initialize CargoManager (you have to implement the contentDelegate first!).
[CargoManager sharedManager].contentDelegate = [GameData sharedData];
// This call sends a product request to CargoBay and caches the resulting products.
[[CargoManager sharedManager] loadStore];
return YES;
}
// contentDelegate should be a singleton object available at all times through the lifetime of your app
- (NSArray *)productIdentifiers
{
NSMutableArray *productIdentifiers = [[NSMutableArray alloc] init];
// Populate the productIdentifiers.
// YOUR CODE GOES HERE
// Return a non-mutable copy.
return [NSArray arrayWithArray:productIdentifiers];
}
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier
{
// Implement the result of a successful IAP
// on the according productIdentifier.
// YOUR CODE GOES HERE
// Save user data to disk.
// YOUR CODE GOES HERE
}
// UIDelegate is optional. Normally, it will be your StoreViewController.
- (void)transactionDidFinishWithSuccess:(BOOL)success
{
if ( success )
{
// Close store UI ?
// YOUR CODE GOES HERE
}
else
{
// Do not close the store ? Further notify the user about the error?
// YOUR CODE GOES HERE
}
}
// You can respond to the CMProductRequestDidReceiveResponseNotification in the following manner.
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reloadStoreProducts:)
name:CMProductRequestDidReceiveResponseNotification
object:[CargoManager sharedManager]];
}
@protocol CargoManagerContentDelegate <NSObject>
// This method should return an array with all the productIdentifiers used by your App.
- (NSArray *)productIdentifiers;
// Implement this method to provide content.
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier;
@optional
// Use this method if you want to store the transaction for your records.
- (void)recordTransaction:(SKPaymentTransaction *)transaction;
// Use this method to manage download data.
- (void)downloadUpdated:(SKDownload *)download;
@end
@protocol CargoManagerUIDelegate <NSObject>
// Implement this method to update UI after a IAP has finished.
// This method is called both for successful and failed transactions.
- (void)transactionDidFinishWithSuccess:(BOOL)success;
@optional
// Implement this method to update UI after a IAP restore has finished.
// This method is called both for successful and failed restores.
- (void)restoredTransactionsDidFinishWithSuccess:(BOOL)success;
@end
CargoManager is used by Trivia Pics Party!.
If your app and company uses CargoManager and want it listed here, just drop me a line.
CargoManager is available under the FreeBSD license so you can use it for commercial applications. See the LICENSE file for more info.
It is maintained by Ricardo Sánchez-Sáez (rsanchez.saez [at] gmail [dot] com).