objective-cには無名クラスが無い(たぶん)。
なのにだいたいインタフェースがオブザーバー。
その都度処理かえるのがめんどい。
ということでBlocksインタフェースをつけちゃいます。
今日はStoreKit。
目標
課金処理の見通しよくする
使う側をこんな感じに
課金処理で結局何がしたいかというと、
・課金をリクエストして成功したか失敗したか。
・リストアをリクエストして成功したか失敗したか
controller.m
// 購入処理
SLSK* slsk = [SLSK getInstance];
slsk.onFail = ^(NSError* error){
NSLog(@"%@", error.debugDescription);
};
slsk.onPurchaseSuccess = ^(NSString* productIdentifier){
NSLog(@"%@ 購入完了", productIdentifier);
};
[slsk puchase:@"com.subakolab.tyarintyariiiiiiin" /*productIdentifier*/];
// リストア
SLSK* slsk = [SLSK getInstance];
slsk.onFail = ^(NSError* error){
NSLog(@"%@", error.debugDescription);
};
// リストア対象のアドオンの数だけ呼ばれる
slsk.onRestoreSuccess = ^(NSString* productIdentifier){
NSLog(@"リストアアドオン:%@", productIdentifier);
};
// リストアが全部終わったら呼ばれる
slsk.onRestoreComplete = ^(){
NSLog(@"リストア完了!");
};
ということでインタフェースはこんな感じ
@property (strong) SLSKOnPurchaseSuccess_t onPurchaseSuccess; @property (strong) SLSKOnFail_t onFail; @property (strong) SLSKOnCancel_t onCancel; @property (strong) SLSKOnRestoreSuccess_t onRestoreSuccess; @property (strong) SLSKOnRestoreComplete_t onRestoreComplete; @property (strong) SLSKOnComplete_t onComplete; // singleton + (SLSK*)getInstance; // 処理 -(void) purchase:(NSString*)productIdentifier; -(void) restore; // 確認 -(BOOL) checkSk; -(BOOL) isConnectiong;
以下、コード
SLSK.h
#import#import typedef enum SLSK_STATE{ SLSK_PRODUCT_REQUEST, SLSK_PURCHASE_REQUEST, SLSK_RESTORE_REQUEST, SLSK_WAIT } SLSKState; typedef enum SLSK_REQUEST_TYPE{ SLSK_REQUEST_PRODUCT, SLSK_REQUEST_PURCHASE, SLSK_REQUEST_RESTORE, SLSK_REQUEST_NONE } SLSKRequestType; typedef void (^SLSKOnPurchaseSuccess_t)(NSString* productIdentifier); typedef void (^SLSKOnFail_t)(NSError* error); typedef void (^SLSKOnCancel_t)(); typedef void (^SLSKOnRestoreSuccess_t)(NSString* productIdentifier); typedef void (^SLSKOnRestoreComplete_t)(); typedef void (^SLSKOnComplete_t)(); @interface SLSK : NSObject< SKProductsRequestDelegate , SKPaymentTransactionObserver >{ // callbacks SLSKOnPurchaseSuccess_t onPurchaseSuccess; SLSKOnFail_t onFail; SLSKOnCancel_t onCancel; SLSKOnRestoreSuccess_t onRestoreSuccess; SLSKOnComplete_t onComplete; SLSKOnRestoreComplete_t onRestoreComplete; // params SLSKState state; SLSKRequestType currentRequestType; NSMutableDictionary* productList; } @property (strong) SLSKOnPurchaseSuccess_t onPurchaseSuccess; @property (strong) SLSKOnFail_t onFail; @property (strong) SLSKOnCancel_t onCancel; @property (strong) SLSKOnRestoreSuccess_t onRestoreSuccess; @property (strong) SLSKOnRestoreComplete_t onRestoreComplete; @property (strong) SLSKOnComplete_t onComplete; // singleton + (SLSK*)getInstance; // 処理 -(void) purchase:(NSString*)productIdentifier; -(void) restore; // 確認 -(BOOL) checkSk; -(BOOL) isConnectiong; @end
SLSK.m
#import "SLSK.h"
@implementation SLSK
@synthesize onPurchaseSuccess;
@synthesize onFail;
@synthesize onComplete;
@synthesize onCancel;
@synthesize onRestoreSuccess;
@synthesize onRestoreComplete;
// ▼singleton ============================
// http://programming-ios.com/objective-c-singleton/ より
+ (SLSK*)getInstance {
static SLSK* sharedSingleton;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedSingleton = [[SLSK alloc]
initInstance];
});
return sharedSingleton;
}
- (id)initInstance {
self = [super init];
if (self) {
[self initialize];
}
return self;
}
- (id)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
// ▲ singleton ===============================
-(void) initialize{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
productList = [[NSMutableDictionary alloc] init];
state = SLSK_WAIT;
currentRequestType = SLSK_REQUEST_NONE;
}
// ▼ SKProductsRequestDelegate =================
// TODO 複数対応
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSArray* products = response.products;
state = SLSK_WAIT;
// productIdentifierが見つからない
if([products count] == 0){
currentRequestType = SLSK_REQUEST_NONE;
NSError* error = [NSError errorWithDomain:@"productIdentifierが無いよerrorDomain"
code:1
userInfo:@{@"debugDescription":@"たぶん無効なproductIdentifier"}];
if(onFail)
onFail(error);
if(onComplete)
onComplete();
return;
}
for(SKProduct* product in products){
if([productList objectForKey:product.productIdentifier] != nil)
continue;
[productList setObject:product forKey:product.productIdentifier];
}
SKProduct* product = [products objectAtIndex:0];
if(currentRequestType == SLSK_REQUEST_PURCHASE){
[self purchaseImple:product];
}
}
// ▼ SKPaymentTransactionObserver ==============
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0){
for(SKPaymentTransaction* transaction in transactions){
switch(transaction.transactionState){
case SKPaymentTransactionStatePurchasing:
NSLog(@"SKPaymentTransactionStatePurchasing");
break;
case SKPaymentTransactionStatePurchased:
NSLog(@"SKPaymentTransactionStatePurchased");
if(onPurchaseSuccess)
onPurchaseSuccess(transaction.payment.productIdentifier);
[self finishRequest:transaction];
break;
case SKPaymentTransactionStateFailed:
NSLog(@"SKPaymentTransactionStateFailed");
if(onFail)
onFail(transaction.error);
[self finishRequest:transaction];
break;
case SKPaymentTransactionStateRestored:
if(onRestoreSuccess)
onRestoreSuccess(transaction.payment.productIdentifier);
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
default:
break;
}
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0){
NSLog(@"removedTransactions");
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0){
NSLog(@"restoreCompletedTransactionsFailedWithError");
if(onFail)
onFail(error);
if(onComplete)
onComplete();
state = SLSK_WAIT;
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0){
NSLog(@"paymentQueueRestoreCompletedTransactionsFinished");
if(onRestoreComplete)
onRestoreComplete();
if(onComplete)
onComplete();
state = SLSK_WAIT;
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_6_0){
NSLog(@"updatedDownloads");
}
// ▼ private ===============================
-(void) finishRequest:(SKPaymentTransaction*) transaction{
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if(onComplete)
onComplete();
state = SLSK_WAIT;
currentRequestType = SLSK_REQUEST_NONE;
}
-(void) purchaseImple:(SKProduct*)product{
state = SLSK_PURCHASE_REQUEST;
SKPayment* payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
-(void) restoreImple{
state = SLSK_RESTORE_REQUEST;
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
-(void) productRequest:(NSString*)productIdentifier{
SKProductsRequest* productRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:[[NSSet alloc] initWithObjects:productIdentifier, nil]];
productRequest.delegate = self;
[productRequest start];
}
-(void) cantSk{
UIAlertView* alertView = [[UIAlertView alloc] init];
alertView.title = @"購入ができません";
alertView.message = @"端末の機能制限でApp内での購入が不可になっています\n\n設定 > 一般 > 機能制限で\n[App内での購入]を\nONにしてください";
[alertView addButtonWithTitle:@"OK"];
[alertView show];
[self cancel];
}
-(void) cancel{
if(onCancel)
onCancel();
if(onComplete)
onComplete();
}
// ▼ public =================================
-(void) purchase:(NSString*)productIdentifier{
if(![self checkSk]){
[self cantSk];
return;
}
if([self isConnectiong]){
NSLog(@"cancel on purchasing.....");
[self cancel];
return;
}
// プロダクト情報が既にあればそれを使う
SKProduct* product = [productList objectForKey:productIdentifier];
if(product != nil) {
[self purchaseImple:product];
return;
}
// なければとりにいく
state = SLSK_PRODUCT_REQUEST;
currentRequestType = SLSK_REQUEST_PURCHASE;
[self productRequest:productIdentifier];
}
-(void) restore{
if(![self checkSk]){
[self cantSk];
return;
}
if([self isConnectiong]){
NSLog(@"cancel on purchasing.....");
[self cancel];
return;
}
state = SLSK_PRODUCT_REQUEST;
currentRequestType = SLSK_REQUEST_RESTORE;
[self restoreImple];
}
-(BOOL) checkSk{
return [SKPaymentQueue canMakePayments];
}
-(BOOL) isConnectiong{
return (state != SLSK_WAIT);
}
@end
参考
Blocks
僕はよくわからないまま使っているので、ちゃんと使いたい方は下記サイトがおすすめです
そんなiphone課金のデザインに興味のある(なくてもOK)デザイナー募集中!
現在スバコラボではデザイナーを【爆裂】で【激求】しています。
お気軽にご応募ください
ご応募はこちらから
0 件のコメント:
コメントを投稿