課éã³ã³ãã³ããAppleã«é ãã - How to implement "Hosting Content with Apple"
iOSã¢ããªå 課éã«ã¤ãã¦ã®è¨äºã§ãã
åç½®ã
iOSã®ã¢ããªå 課éãå®è£ ããã¨ãããããã¯ãï¼èª²éã¢ã¤ãã ï¼ãã©ã®ããã«æä¾ããããæ¤è¨ãå¿ è¦ã«ãªãã¾ããããã¯ãã£ã¦ãæ¡æã§ããæ¹æ³ã¯æ¬¡ã®2ã¤ã«ãªãã®ã§ããã
- ãããã¯ããã¢ããªã«å å ã課éå¾ããã使ç¨ã§ããå®è£ ã«ãã
- ãããã¯ããã¢ããªå¤ï¼å ¬éãµã¼ãçï¼ã«é ç½®ã課éå¾ã«ãã¦ã³ãã¼ãã使ç¨ã§ããããã«ãã
åè ã¯ã軽éãªã³ã³ãã³ããæä¾ããå ´åãã課éã¢ã¤ãã ã®å®ä½ãè¦ããªãå ´åã§æ¡ç¨ããããã¨ãå¤ãã§ãã
対ãã¦å¾è ã¯ãæ¯è¼ç大ããªã³ã³ãã³ããæä¾ããã¨ãã«ä½¿ããã¾ãããã¨ãã°ã³ã³ãã³ããã²ã¼ã ã®è¿½å ã¹ãã¼ã¸ã®å ´åãã¹ãã¼ã¸ã®ç»åãã¼ã¿ãBGMãªã©ãå«ã¾ããã§ããããããããã¢ããªã«å å ãã¦ãã¾ãã¨ãã¢ããªã®ãµã¤ãºãè¥å¤§åãã¢ããªãã¦ã³ãã¼ãã«åé¡ãçããå¯è½æ§ãããã¾ãï¼ã¢ãã¤ã«åç·ã§ãã¦ã³ãã¼ãå¯è½ãªiOSã¢ããªã®ãµã¤ãºã¯100MBã¾ã§ã§ãï¼ã
ä¾ã¨ãã¦ãiOSã¢ããªã®ã太é¼ã®é人ãã¯ãã¢ããªå
課éã§æ¥½æ²ã追å è³¼å
¥ãããã¨ãã§ããè³¼å
¥å¾ã«æ¥½æ²ãã¼ã¿ãå
¬éãµã¼ããããã¦ã³ãã¼ããã¦ãã¾ãã
ã³ã³ãã³ã管çãµã¼ããç¨æããï¼
ãã¦ããããã¯ããã¢ããªå¤ã«é
ç½®ã課éå¾ã«ãã¦ã³ãã¼ãã§ããããã«ããããã«ã¯ããããã¯ãã®é
ç½®å
ã¨ãªãå
¬éãµã¼ããå¿
è¦ã«ãªãã¾ããã¾ããããã¯ããåç´ã«é
ç½®ãã¦ãã¾ãã¨ãè³¼å
¥ãã¦ããªãã¦ã¼ã¶ããã¦ã³ãã¼ãã§ãã¦ãã¾ãã¾ãã®ã§ãæ£è¦è³¼å
¥è
ã ãããã¦ã³ãã¼ãã§ãããããªã»ãã¥ãªãã£ã¼ã®å®è£
ï¼Webã¢ããªã±ã¼ã·ã§ã³ã®å®è£
ãªã©ï¼ãå¿
è¦ã«ãªãã¾ãã
ãããå
¬éãµã¼ãã®éç¨ã¯ãå¤ãã®å ´åã§ã³ã¹ãããããã¾ããç§ãããã§ãããå人ã®éçºè
ã«ã¨ã£ã¦ããã足ããã«ãªããã¨ãå¤ãããããã«æãã¾ããããã¦ãããããã«ããã¯ã¨ãªãããããã¯ãã®æä¾ãæãããã«ã§ããªããã¨ãããããããã¾ããã
ã³ã³ãã³ã管çãµã¼ããä¸è¦ãª "Hosting Content with Apple"
ãã®ããã«ããã¯ã«ã¤ãã¦Appleã解決çãæä¾ãã¦ãã¾ãããã¦ã³ãã¼ãããããããã¯ããAppleã®ãµã¼ãã«é ããï¼Hostingããï¼ãã¨ãã§ãã "Hosting Content with Apple" ã§ãã"Hosting Content with Apple" ãå©ç¨ãããã¨ã§ãéçºè
ã¯å
¬éãµã¼ãã®éç¨ãã»ãã¥ãªãã£ã¼ã®å®è£
ãçç¥ã§ããiOSã¢ããªã®éçºã ãã«éä¸ãããã¨ãã§ãã¾ãã
æé
"Hosting Content with Apple" ã¯ã次ã®æé ã¨éçºçç£ç©ã§å©ç¨ã§ããããã«ãªãã¾ãã
- iTunes Connectã« "Hosting Content with Apple" ã®ãããã¯ããç»é²ãã
- Xcode㧠"Hosting Content with Apple" ã®ãããã¯ããçæ
- Xcode㧠"Hosting Content with Apple" ã®ãããã¯ããiTunes Connectã¸ã¢ãããã¼ã
- ã¢ããªå 課éã®å®è£ ã«ã"Hosting Content with Apple"ãããã¯ãã®ãã¦ã³ãã¼ãã追å
1. iTunes Connectã« "Hosting Content with Apple" ã®ãããã¯ããç»é²ãã
iTunes Connectãããæ¶èåãããã¯ãããéæ¶èåãããã¯ãããç»é²ããæé ã¨ã»ã¼å¤ããã¾ããããç¾ç¶ "Hosting Content with Apple" ã«å¯¾å¿ãã¦ããã®ã¯ãéæ¶èåãããã¯ããã ãã§ãã
iTunes Connectã«ãã°ã¤ã³ã [Manage Your Apps] --> [ã¢ããªé¸æ] --> [Manage In-App Purchases] --> [Create New]ã¨é²ã¿ãæ°è¦ãããã¯ãã®ç»é²ã«é²ã¿ã¾ããããã¦ãéæ¶èåãããã¯ãããç»é²ãããã [Non-Consumable]ãé¸æãã¾ãã
[Product ID]ã[Price Tier]ãå
¥åãã¦ããã¾ããä»åã¯ãããã¯ãè³¼å
¥å¾ã«ç»åãã¼ã¿ããã¦ã³ãã¼ããããæ³å®ã§ "Picture" ã¨ããIDãä»ãã¾ããã
ç¶ãã¦[Language]ã¨[Hosting Content with Apple]ãè¨å®ãã¦ããã¾ããå¤ãã®ä¾ã§ã¯[Hosting Content with Apple]ã«"No"ãæå®ãã¾ãããä»åã¯"Yes"ã«ãã¾ãã
ãã¨ã¯é©å½ãªã¹ã¯ãªã¼ã³ã·ã§ãããã¢ãããã¼ããã¦ç»é²å®äºã§ããç»é²æ¸ã¿ãããã¯ãã®ä¸è¦§ã«"Picture"ã追å ãã[Status]ã"Waiting for Upload"ï¼ãããã¯ãã®ã¢ãããã¼ãå¾
ã¡ï¼ã«ãªã£ã¦ãããã¨ã確èªãã¾ãããã
iTunes Connectã¸ã®ãããã¯ãç»é²ã¯ä»¥ä¸ã§ãã
2. Xcode㧠"Hosting Content with Apple" ã®ãããã¯ããçæ
ããããXcodeã使ã£ã¦å®è£
ãé²ãã¾ããã¾ãã¯ã¢ãããã¼ããããããã¯ããä½ãã¾ãã
æ°è¦Projectã®ãã³ãã¬ã¼ãã¯[iOS] --> [Other] --> [In-App Purchase Content]ãé¸æãã¾ãã
[Product Name]ã¨[Company Identifier]ãå
¥åãã¾ããããã¯ä»»æã®å¤ã§æ§ãã¾ããã
ããã§Projectãçæã§ãã¾ããã
Projectã®åæç¶æ
ã§ã¯ [Product Identifier]ã [Company Identifier]+[Product Name]ã«ãªã£ã¦ãã¾ãã
å®ã¯ãããéè¦ã§ããã®å¤ã¯iTunes Connectã«ç»é²ããã¨ãã®[Product ID]ã«åãããå¿
è¦ãããã¾ããããã§ãããæ¸ãæãã¦ããã¾ãã
[ContentInfo.plist]ãé¸æã[IAPProductIrentifier]ãåç
§ãã¾ãã
"${PRODUCT_NAME:rfc1034identifier}"ã¨ãªã£ã¦ããåæå¤ããiTunes Connectã«ç»é²ãã[Product ID]ã¨åã "Picture" ã¨ããåºå®å¤ã«æ¸ãæãã¾ãã
ããã¦ãè³¼å
¥å¾ã«ãã¦ã³ãã¼ãããããã³ã³ãã³ãï¼ï¼ãã¡ã¤ã«ï¼ã追å ãã¾ãã[Supporting Files]ã°ã«ã¼ãã®ä¸ã«ãã¡ã¤ã«ã追å ãã¾ãããã¼ã¿ã®ãã©ã¼ãããããã¡ã¤ã«åã¯ä»»æã§æ§ãã¾ããã
ãã«ããã¦ã¨ã©ã¼ãåºãªããã°ããããã¯ãã®çæã¯å®äºã§ãã
3. Xcode㧠"Hosting Content with Apple" ã®ãããã¯ããiTunes Connectã¸ã¢ãããã¼ã
å¼ãç¶ãXcodeã§ç¶ãã¾ããã¡ãã¥ã¼ãã[Product] --> [Archive]ãé¸æããã¢ãããã¼ãããã³ã³ãã³ããåºåãã¾ãã
Archiveå¾ã«[Organizer] --> [Archives]ã表示ããã¨ãArchiveåºåããã¦ãããã¨ã確èªã§ãã¾ããã¾ã[Status]åã空ã«ãªã£ã¦ããã¨æãã¾ãã
Archiveããã³ã³ãã³ããã¢ãããã¼ããã¦ããã¾ããOrganizerã®[Distribute]ãã¯ãªãã¯ãã¾ãã
"Select the method of distribution:"ï¼é
å¸æ¹æ³ã®é¸æï¼ã¯[Submit in-app purchase content]ãé¸æãã¾ãã
"Log in to iTunes Connect:" ã§ã¯iTunes Connectã«ãããã¯ããç»é²ããã¨ãã¨åãã¢ã«ã¦ã³ãæ
å ±ãå
¥åãã¾ãã
"Choose in-app purchase content record:" ã§ã¯ãiTunes Connectã«ç»é²æ¸ã¿ã®Hosting Content with AppleãYesã«ãªã£ã¦ããProduct IDã表示ããã¾ããã¢ãããã¼ãå
ã®Product IDãé¸æãã¾ãã
[Submit]ãã¯ãªãã¯ããã¨ã¢ãããã¼ããå§ã¾ãããã°ããããã¨å®äºãã¾ãã
"No issures ware found"ï¼åé¡ç¡ãï¼ã®ã¡ãã»ã¼ã¸ãåºã¦ããã°ã¢ãããã¼ãå®äºã§ãã
Organizerã«æ»ããArchiveã®[Status]ã"Submitted"ã«ãªã£ã¦ãã¾ããã
ã¾ãiTunes Connectã"Processing Content"ã«å¤ãã£ã¦ããã¯ãã§ãã
ãããã¯ãã®ã¢ãããã¼ãã¯ããã§å®äºã§ãã*1
4. ã¢ããªå 課éã®å®è£ ã«ã"Hosting Content with Apple"ãããã¯ãã®ãã¦ã³ãã¼ãã追å
ã·ã¼ã±ã³ã¹ã¯æ¬¡ã®ããã«ãªãã¾ãã
SKpaymentQueueã®startDownloadsãã³ã¼ã«ããã¨StoreKitã«ãããã¦ã³ãã¼ããå®è¡ããããã¦ã³ãã¼ãã®ç¶æ³ããã¦ã³ãã¼ããããã¼ã¿ã (void)paymentQueue:queue updatedDownloads:downloads ã«éç¥ããã¾ãã
ã¾ãã¢ããªå
課éã®è³¼å
¥å®äºå¾ã«ããã¦ã³ãã¼ãå¦çã追å ãã¾ãã
(ããããåã®å¦çã«ã¤ãã¦ã¯ãã®æ¬ãã覧ãã ãã http://tworks.hatenablog.jp/entry/2014/01/01/000500 )
// StoreKit // è³¼å ¥ããªã¹ãã¢ãªã©ã®ãã©ã³ã¶ã¯ã·ã§ã³ã®é½åº¦ãéç¥ããã - (void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { NSLog(@"paymentQueue:updatedTransactions"); for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { // è³¼å ¥å¦çå®äº case SKPaymentTransactionStatePurchased: { NSLog(@"SKPaymentTransactionStatePurchased"); // ProductãHosting Contentãªã®ãå¤å® if (transaction.downloads) { // Hosting Contentãªã®ã§ãã¦ã³ãã¼ããè¡ã [queue startDownloads:transaction.downloads]; } else { // Hosting Contentã§ã¯ãªãã®ã§ãè³¼å ¥ãå®äºãã [queue finishTransaction:transaction]; } break; } â¦
Hosting Contentã«å¯¾å¿ãããããã¯ãã¯transaction.downloadsãYesã«ãªãã¾ããããããã£ã¦ãã¦ã³ãã¼ããè¡ããå¦ããå¤å®ãã¾ãã
ãã¦ã³ãã¼ããå§ã¾ãã¨ããã®ç¶æ³ã paymentQueue:queue updatedDownloads:downloads ã«éç¥ããã¾ãããã¦ã³ãã¼ãã®ç¶æ³ã¯ download.downloadState ãè¦ã¦å¤å®ãããã¨ãã§ãã¾ãããã¦ã³ãã¼ãé²è¡ä¸ã¯"SKDownloadStateActive"ããã¦ã³ãã¼ãå®äºã§"SKDownloadStateFinished"ã¨ãªãã¾ãã
// ãã¦ã³ãã¼ãéç¥å¦ç - (void)paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads { for (SKDownload *download in downloads) { if (download.downloadState == SKDownloadStateFinished) { [self processDownload:download]; // ãã¦ã³ãã¼ãå¦ç // [queue finishTransaction:download.transaction]; } else if (download.downloadState == SKDownloadStateActive) { NSTimeInterval remaining = download.timeRemaining; // secs float progress = download.progress; // 0.0 -> 1.0 NSLog(@"%lf%% (æ®ã %lf ç§)", progress, remaining); } else { // waiting, paused, failed, cancelled NSLog(@"ãã¦ã³ãã¼ãä¸æåæ¢ã¾ãã¯ãã£ã³ã»ã«ãæ¤åº: %ld", (long)download.downloadState); } } }
ãã¦ã³ãã¼ãå®äºæãã³ã³ãã³ããä¿åããããã«ãã¾ãããããã¦ã³ãã¼ãå ã®Pathã¨ä¿åå ã®Pathãä½æãããã¡ã¤ã«ãä¿åãä¿åå ã®Pathãè¨å®ï¼NSUserDefaultsï¼ã«è¨æ¶ãã¦ããã¾ãã
- (void)processDownload:(SKDownload *)download { // NSFileManager NSString *path = [download.contentURL path]; // ãã¦ã³ãã¼ãããã³ã³ãã³ãã®ãã£ã¬ã¯ã㪠path = [path stringByAppendingPathComponent:@"Contents"]; NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; NSArray *files = [fileManager contentsOfDirectoryAtPath:path error:&error]; NSString *dir = [self downloadableContentPath]; for (NSString *file in files) { NSString *fullPathSrc = [path stringByAppendingPathComponent:file]; NSString *fullPathDst = [dir stringByAppendingPathComponent:file]; // ä¸æ¸ãã§ããªãã®ã§ä¸æ¦åé¤ [fileManager removeItemAtPath:fullPathDst error:NULL]; // ãã¦ã³ãã¼ãå ããä¿åå ã¸ãã¡ã¤ã«ã移åãã if ([fileManager moveItemAtPath:fullPathSrc toPath:fullPathDst error:&error] == NO) { NSLog(@"Error: ãã¡ã¤ã«ã®ç§»åã«å¤±æ: %@", error); } // è¨å®ã«ãããã¯ãIDãä¿æ [[NSUserDefaults standardUserDefaults] setObject:fullPathDst forKey:download.transaction.payment.productIdentifier]; [[NSUserDefaults standardUserDefaults] synchronize]; } } - (NSString *)downloadableContentPath { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); NSString *directory = [paths objectAtIndex:0]; directory = [directory stringByAppendingPathComponent:@"Downloads"]; NSFileManager *fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:directory] == NO) { NSError *error; if ([fileManager createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:&error] == NO) { NSLog(@"Error: ãã£ã¬ã¯ããªä½æ失æ: %@", error); } NSURL *url = [NSURL fileURLWithPath:directory]; // iCloud backupãããã¦ã³ãã¼ãããã³ã³ãã³ããæé¤ããªãã¨ããªã¸ã§ã¯ã対象ã«ãªãã®ã§æ³¨æ if ([url setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error] == NO) { NSLog(@"Error: iCloud backup対象é¤å¤ã失æ: %@", error); } } return directory; }
ãã¨ã¯ãä¿åãããã¡ã¤ã«ãUIå´ã§è¡¨ç¤ºããããã¼ã¿åç
§ããã ãã§ããã
ã¾ã¨ã
"Hosting Content with Apple"ã¯ããã®ããã«æé ã¨å®è£ ããæ¼ããã¦ãã¾ãã°ãç°¡åã«æ±ããã¨ãã§ãããã®ã§ãã使ç¨ããããã«è¿½å ã§çºçããã³ã¹ããããã¾ããï¼iOS Developer Programã®ã³ã¹ãã«å«ã¾ãã¾ãï¼ããããããã課éã³ã³ãã³ããå¤é¨ãµã¼ãã«é ç½®ããã«ããã "Hosting Content with Apple" ã使ããªãçç±ã¯ç¹ã«ãªãããã«æãã¾ãã
æå¾ã«
ã»ä»åã®ãµã³ãã«ã¯githubã«å
¬éãã¦ãã¾ãã
ãhttps://github.com/tomohba/hosting_content_with_apple_sample
ã»æä¸ã®ã¹ã«ã¡ã¤ã«ç»å㯠id:ch3cooh393 製ã§ããããããã
ã»iOSã¢ããªå
課éã®å
¨è¬ã¯ãèè
ã®èæ¸ãã覧ãã ããã
ãhttp://tworks.hatenablog.jp/entry/2014/01/01/000500