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 件のコメント:
コメントを投稿