2012-10-21 13 views
21

मैंने ऐप्पल होस्टेड इन-ऐप खरीद सेट अप करने के लिए http://www.raywenderlich.com/21081/introduction-to-in-app-purchases-in-ios-6-tutorial का पालन किया। यह उत्पादों की सूची है। जब मैं एप्पल से उत्पादों को डाउनलोड करना चाहते है, मैं इसइन-ऐप होस्ट की गई सामग्री को कैसे डाउनलोड करें?

-(void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions 
{ 
    for (SKPaymentTransaction * transaction in transactions) 
    { 
     switch (transaction.transactionState) 
     { 
      case SKPaymentTransactionStatePurchased: 
      { 
       [[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads]; 

    .... 

} 

-(void) paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads 
{ 
    NSLog(@"paymentQues"); 

    for (SKDownload *download in downloads) 
    { 
     switch (download.downloadState) 
     { 
      case SKDownloadStateActive: 
      { 
       NSLog(@"%f", download.progress); break; 
      } 
    ... 

} 

-(void) paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions 
{ 

} 

की तरह कुछ मैं updatedTransactions में डाउनलोड शुरू करते हैं, तो updatedDownloads downloadState साथ एप्पल द्वारा कहा जाता है == सक्रिय। फिर अगला, ऐप्पल कॉल को हटा देता है बिना किसी वास्तव में डाउनलोड शुरू किए बिना लेनदेन। डाउनलोड प्रगति हमेशा 0% और अपडेट की जाती है डाउनलोड डाउनलोड को कभी भी डाउनलोडस्टेट == समाप्त नहीं किया जाता है।

मैं क्यों मेरे डाउनलोड शुरू कर दिया कभी नहीं पता नहीं है और मेरे लेन-देन डाउनलोड समाप्त होने के पहले भी निकल जाते हैं। किसी के पास एक कामकाजी नमूना है?

+0

मुझे एक ही समस्या है ... –

उत्तर

35

समस्या यह है कि मैं स्पष्ट रूप से लेन-देन बंद करने के लिए भूल गया है। संदर्भ के लिए मेरा पूरा कोड इस प्रकार है। इसमें अन्य चीजें हैं जैसे कि डाउनलोड करते समय प्रगति पट्टी प्रदर्शित करना, लेकिन यह 100% काम कर रहा है। Utility.h के बारे में चिंता न करें, यह केवल कुछ मैक्रोज़ को परिभाषित करता है जैसे SAFE_RELEASE_VIEW।

अनिवार्य रूप से मैं परिभाषित करने दो तरीकों खरीद सकते हैं और डाउनलोड द्वारा raywenderlich में नमूना बढ़ाया।

updatedDownloads को वेतन ध्यान। एक बार डाउनलोड समाप्त होने के बाद, मैं सामग्री को उपयोगकर्ता की दस्तावेज़ निर्देशिका में कॉपी करता हूं। आप एप्पल से डाउनलोड करते हैं, निर्देशिका आप इस तरह है:

    • ContentInfo.plist
      • सामग्री
        • अपनी फ़ाइलें

ऐप्पल केवल आपको डाउनलोड फ़ोल्डर का मार्ग देता है। आप ContentInfo.plist पढ़ने के लिए पथ का उपयोग करते हैं। मेरे ऐप में, मेरे पास ContentInfo.plist में एक संपत्ति "फ़ाइलें" है जो सामग्री फ़ाइलों में मेरी फ़ाइलों को सूचीबद्ध करती है। मैं फिर फ़ाइलों को दस्तावेज़ फ़ोल्डर में कॉपी करता हूं। यदि आप ऐसा नहीं करते हैं, तो आपको अनुमान लगाया जाना चाहिए कि आपके सामग्री फ़ोल्डर में कौन सी फाइलें हैं, या आप बस सबकुछ अंदर कॉपी करते हैं।

यह SmallChess के लिए वास्तविक इन-ऐप खरीदारी कोड है (http://www.smallchess.com)।

#import <StoreKit/StoreKit.h> 
#import "MBProgressHUD/MBProgressHUD.h" 
#import "Others/Utility.h" 
#import "Store/OnlineStore.h" 

NSString *const ProductPurchasedNotification = @"ProductPurchasedNotification"; 

@implementation StoreTransaction 
@synthesize productID, payment; 
@end 

@interface OnlineStore() <SKProductsRequestDelegate, SKPaymentTransactionObserver, MBProgressHUDDelegate> 
@end 

@implementation OnlineStore 
{ 
    NSSet *_productIDs; 
    MBProgressHUD *_progress; 
    NSMutableSet * _purchasedIDs; 
    SKProductsRequest * _productsRequest; 
    RequestProductsCompletionHandler _completionHandler; 
} 

-(id) init 
{ 
    if ([SKPaymentQueue canMakePayments] && (self = [super init])) 
    { 
     [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; 
    } 

    return self; 
} 

#pragma mark MBProgressHUDDelegate 

-(void) hudWasHidden:(MBProgressHUD *)hud 
{ 
    NSAssert(_progress, @"ddd"); 

    [_progress removeFromSuperview]; 

     SAFE_RELEASE_VIEW(_progress); 
} 

#pragma end 

#pragma mark SKProductsRequestDelegate 

-(void) request:(NSSet *)productIDs handler:(RequestProductsCompletionHandler)handler 
{  
    _completionHandler = [handler copy]; 

    _productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIDs]; 
    _productsRequest.delegate = self; 
    [_productsRequest start];  
} 

-(void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response 
{ 
    _productsRequest = nil; 
    _completionHandler(YES, response.products); 
    _completionHandler = nil; 
} 

-(void) request:(SKRequest *)request didFailWithError:(NSError *)error 
{ 
    NSLog(@"Failed to load list of products."); 
    _productsRequest = nil; 

    _completionHandler(NO, nil); 
    _completionHandler = nil; 
} 

#pragma end 

#pragma mark Transaction 

-(void) provideContentForProduct:(SKPaymentTransaction *)payment productID:(NSString *)productID 
{ 
    [_purchasedIDs addObject:productID]; 

    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:productID]; 
    [[NSUserDefaults standardUserDefaults] synchronize]; 

    StoreTransaction *transaction = [[StoreTransaction alloc] init]; 

    [transaction setPayment:payment]; 
    [transaction setProductID:productID]; 

    [[NSNotificationCenter defaultCenter] postNotificationName:ProductPurchasedNotification object:transaction userInfo:nil]; 
} 

-(void) completeTransaction:(SKPaymentTransaction *)transaction 
{ 
#ifdef DEBUG 
    NSLog(@"completeTransaction"); 
#endif 

    [self provideContentForProduct:transaction productID:transaction.payment.productIdentifier]; 
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; 
} 

-(void) restoreTransaction:(SKPaymentTransaction *)transaction 
{ 
#ifdef DEBUG 
    NSLog(@"restoreTransaction"); 
#endif 

    [self provideContentForProduct:transaction productID:transaction.originalTransaction.payment.productIdentifier]; 
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; 
} 

-(void) failedTransaction:(SKPaymentTransaction *)transaction 
{ 
#ifdef DEBUG 
    NSLog(@"failedTransaction"); 
#endif 

    if (transaction.error.code != SKErrorPaymentCancelled) 
    { 
     NSLog(@"Transaction error: %@", transaction.error.localizedDescription); 
    } 

    [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; 
} 

-(void) restoreCompletedTransactions 
{ 
#ifdef DEBUG 
    NSLog(@"restoreCompletedTransactions"); 
#endif 

    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; 
} 

#pragma end 

#pragma mark Buy & Download 

-(BOOL) purchased:(NSString *)productID 
{ 
    return [_purchasedIDs containsObject:productID]; 
} 

-(void) buy:(SKProduct *)product 
{ 
    SKPayment * payment = [SKPayment paymentWithProduct:product]; 
    [[SKPaymentQueue defaultQueue] addPayment:payment]; 
} 

-(void) download:(StoreTransaction *)transaction 
{ 
    NSAssert(transaction.payment.transactionState == SKPaymentTransactionStatePurchased || 
      transaction.payment.transactionState == SKPaymentTransactionStateRestored, @"The payment transaction must be completed"); 

    if ([transaction.payment.downloads count]) 
    { 
     [[SKPaymentQueue defaultQueue] startDownloads:transaction.payment.downloads]; 
    } 
} 

#pragma end 

#pragma mark SKPaymentTransactionObserver 

-(void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue 
{ 
    NSLog(@"RestoreCompletedTransactions"); 
} 

-(void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions 
{ 
    for (SKPaymentTransaction * transaction in transactions) 
    { 
     switch (transaction.transactionState) 
     { 
      case SKPaymentTransactionStatePurchased: 
      { 
#ifdef DEBUG 
       NSLog(@"SKPaymentTransactionStatePurchased"); 
#endif 

       [[SKPaymentQueue defaultQueue] startDownloads:transaction.downloads]; 
       break; 
      } 

      case SKPaymentTransactionStateFailed: 
      { 
       NSLog(@"Failed"); 
       [self failedTransaction:transaction]; 
       break; 
      } 

      case SKPaymentTransactionStateRestored: 
      { 

       NSLog(@"Restored"); 
       [self restoreTransaction:transaction]; break; 
      } 

      case SKPaymentTransactionStatePurchasing: 
      { 
#ifdef DEBUG 
       NSLog(@"SKPaymentTransactionStatePurchasing"); 
#endif 

       break; 
      } 
     } 
    } 
} 

-(void) paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error 
{ 
#ifdef DEBUG 
    NSLog(@"restoreCompletedTransactionsFailedWithError"); 
#endif 
} 

-(void) paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions 
{ 
#ifdef DEBUG 
    NSLog(@"removedTransactions"); 
#endif 
} 

-(void) paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads 
{ 
    for (SKDownload *download in downloads) 
    { 
     switch (download.downloadState) 
     { 
      case SKDownloadStateActive: 
      { 
#ifdef DEBUG 
       NSLog(@"%f", download.progress); 
       NSLog(@"%f remaining", download.timeRemaining); 
#endif 

       if (download.progress == 0.0 && !_progress) 
       { 
        #define WAIT_TOO_LONG_SECONDS 60 
        #define TOO_LARGE_DOWNLOAD_BYTES 4194304 

        const BOOL instantDownload = (download.timeRemaining != SKDownloadTimeRemainingUnknown && download.timeRemaining < WAIT_TOO_LONG_SECONDS) || 
               (download.contentLength < TOO_LARGE_DOWNLOAD_BYTES); 

        if (instantDownload) 
        { 
         UIView *window= [[UIApplication sharedApplication] keyWindow]; 

         _progress = [[MBProgressHUD alloc] initWithView:[[UIApplication sharedApplication] keyWindow]]; 
         [window addSubview:_progress]; 

         [_progress show:YES]; 
         [_progress setDelegate:self]; 
         [_progress setDimBackground:YES]; 
         [_progress setLabelText:@"Downloading"]; 
         [_progress setMode:MBProgressHUDModeAnnularDeterminate]; 
        } 
        else 
        { 
         NSLog(@"Implement me!"); 
        } 
       } 

       [_progress setProgress:download.progress]; 

       break; 
      } 

      case SKDownloadStateCancelled: { break; } 
      case SKDownloadStateFailed: 
      { 
       [Utility showAlert:@"Download Failed" 
          message:@"Failed to download. Please retry later" 
         cancelTitle:@"OK" 
         otherTitle:nil 
          delegate:nil]; 
       break; 
      } 

      case SKDownloadStateFinished: 
      { 
       NSString *source = [download.contentURL relativePath]; 
       NSDictionary *dict = [[NSMutableDictionary alloc] initWithContentsOfFile:[source stringByAppendingPathComponent:@"ContentInfo.plist"]]; 

       if (![dict objectForKey:@"Files"]) 
       { 
        [[SKPaymentQueue defaultQueue] finishTransaction:download.transaction]; 
        return; 
       } 

       NSAssert([dict objectForKey:@"Files"], @"The Files property must be valid"); 

       for (NSString *file in [dict objectForKey:@"Files"]) 
       { 
        NSString *content = [[source stringByAppendingPathComponent:@"Contents"] stringByAppendingPathComponent:file]; 

        NSAssert([Utility isFileExist:content], @"Content path must be valid"); 

        // Copy the content to the Documents folder, don't bother with creating a directory for it 
        DEFINE_BOOL(succeed, [Utility copy:content dst:[[Utility getDocPath] stringByAppendingPathComponent:file]]); 

        NSAssert(succeed, @"Failed to copy the content"); 

#ifdef DEBUG 
        NSLog(@"Copied %@ to %@", content, [[Utility getDocPath] stringByAppendingPathComponent:file]); 
#endif 
       } 

       if (download.transaction.transactionState == SKPaymentTransactionStatePurchased && _progress) 
       { 
        [Utility showAlert:@"Purchased Complete" 
           message:@"Your purchase has been completed. Please refer to the FAQ if you have any questions" 
          cancelTitle:@"OK" 
          otherTitle:nil 
           delegate:nil]; 
       } 

       [_progress setDimBackground:NO]; 
       [_progress hide:YES]; 

       [[SKPaymentQueue defaultQueue] finishTransaction:download.transaction]; 
       break; 
      } 

      case SKDownloadStatePaused: 
      { 
#ifdef DEBUG 
       NSLog(@"SKDownloadStatePaused"); 
#endif 
       break; 
      } 

      case SKDownloadStateWaiting: 
      { 
#ifdef DEBUG 
       NSLog(@"SKDownloadStateWaiting"); 
#endif 
       break; 
      } 
     } 
    } 
} 

#pragma end 

@end 
+0

ग्रेट पोस्ट। क्यू एंड ए दोनों को ऊपर उठाया गया, हालांकि, जाहिर है कि आप यह नहीं कह रहे हैं कि आप क्या करते हैं जब आप कहते हैं: "समस्या यह है कि मैं लेनदेन को स्पष्ट रूप से बंद करना भूल गया।" मुझे डाउनलोड स्थिति की एक ही समस्या का सामना करना पड़ रहा है जो कभी पूरा नहीं हो रहा है। सिम्युलेटर पर डिबगिंग हालांकि मेरे पास अभी मेरे साथ असली डिवाइस नहीं है, बस घबराहट हो रही है। क्या आप कुछ प्रकाश डाल सकते हैं? –

+0

मैं आपको यह नहीं बता सकता कि यह सिम्युलेटर में काम करेगा क्योंकि मैंने कभी कोशिश नहीं की, लेकिन कम से कम पहले आईओएस में, यह काम नहीं करेगा। नवीनतम आईओएस के बारे में निश्चित नहीं है। – SmallChess

+0

प्रश्न: क्या आपका डाउनलोड भी शुरू हुआ है? अर्थात। क्या यह कभी 0% के अलावा कुछ भी पहुंच गया है? मेरे कोड में, मेरे पास है: NSLog (@ "% f", download.progress) ;, कृपया वही करें और मुझे बताएं कि आपके पास क्या है। – SmallChess

2

स्वीकारना इस अपने विशेष समस्या का जवाब नहीं है,

मैं इस क्षेत्र

  1. इस विधि में कुछ अन्य समस्याओं का सामना करना पड़ा के दौरान कई बार कहा जाता है पुनर्स्थापित/खरीद चक्र, केवल एक बार नहीं।
  2. यह भी अपने मंगलाचरण के बिना एप्लिकेशन को पुन: प्रारंभ पर कहा जा सकता है (बाधित पुनर्स्थापित/डाउनलोड जारी रखने के लिए)।
  3. यदि आप केवल एक उत्पाद इंडेंटिफायर को पुनर्स्थापित करना चाहते हैं, तो आपको अन्य सभी को फ़िल्टर करना होगा।
  4. आप startDownloads बुलाना चाहिए नहीं करता है, तो डाउनलोड राज्य प्रतीक्षा कर रहा है नहीं है/
  5. रोका गया कभी कभी, अगर आप startDownloads फोन, आप एक ही लेन-देन के लिए updatedTransaction को एक और कॉल प्राप्त कर सकते हैं और यह है download.downloadState अभी भी हो जाएगा प्रतीक्षा कर रहा है - और यदि आप कॉल को दूसरी बार डाउनलोड करते हैं तो आप डाउनलोड प्रगति/पूर्ण अधिसूचना दो बार प्राप्त कर सकते हैं। यदि डाउनलोड है तो असफल समस्याएं फ़ाइलों की प्रतिलिपि बनाने से पहले लक्षित स्थान को साफ़ कर सकती हैं। क्योंकि दूसरा डाउनलोड वास्तव में कैश पर डाउनलोड नहीं होता है, इसलिए आपके पास दूसरी अधिसूचना पर प्रतिलिपि बनाने के लिए कुछ भी नहीं है। मैंने डाउनलोड के स्थानीय सरणी को रिकॉर्ड करके इस पर काम किया है, मुझे पता है कि मैंने स्टार्टडाउनलोड को कॉल किया है, और पूरी तरह से पूरा किया है।

मैंने अपने स्टोर हैंडलर में व्यापक डीबग कथन और इस व्यवहार को साबित करने के लिए प्रत्येक केस स्टेटमेंट डाला है, और सुनिश्चित किया है कि मैं कतार को एक से अधिक बार नहीं बुला रहा हूं। मेरा सुझाव है कि कोई भी शुरू करने वाला वही करता है - डायग्नोस्टिक्स में डालें।

यह इस तथ्य से मदद नहीं करता है कि एसकेडाउनलोड, एसकेपेमेंट ट्रान्सएक्शन में पर्याप्त विवरण विधियां नहीं हैं, आपको अपना खुद का रोल करना होगा।

वैकल्पिक रूप से जिथब पर किसी और के स्टोर हैंडलर का उपयोग करें।

+1

ऐप्पल के इन-खरीद कोड को अच्छी तरह लागू नहीं किया गया है, हर कोई इसे जानता है। लेकिन आपके कुछ अवलोकन वास्तव में दस्तावेज किए गए हैं, उदाहरण के लिए, (1) में हम वर्तमान प्रगति की जांच के लिए इसका उपयोग करते हैं। हालांकि विधियों को अच्छी तरह से प्रलेखित नहीं किया गया है, इसके लिए बाहरी लाइब्रेरी का उपयोग करना इसके लिए थोड़ा अधिक है। – SmallChess

+0

हां मैं प्रगति की निगरानी के लिए अद्यतन डाउनलोड का उपयोग करता हूं यदि आपने सोचा कि मेरा मतलब है। लेकिन मैंने अपडेट की उम्मीद नहीं की थी ट्रांजैक्शन को ऑब्जेक्ट्स की एक ही स्थिति के लिए कई बार बुलाया जा सकता है - जो ऐसा प्रतीत होता है। मैं स्वीकार करता हूं कि मैं कुछ गलत कर रहा हूं, लेकिन मुझे लगता है कि इन दिनोंचर्या से नरक को डीबग करने और आक्रामक परीक्षण परिदृश्यों को विकसित करने की समग्र सलाह है (जैसे ऐप मिड डाउनलोड/पुनर्स्थापना छोड़ना), मुझे अच्छी सलाह है! – GilesDMiddleton

+0

मुझे वही निराशा थी जैसे कि आप कुछ साल पहले इस पोस्ट को लिखते समय वापस आ रहे थे। मुझे याद है कि मैं घंटों और घंटों तक बैठ गया और डीबग किया। लेकिन आप इसका उपयोग करेंगे .... विधि अद्यतन किया गया है ट्रांज़ेक्शन को कई बार बुलाया जाने की उम्मीद है, इसे "अद्यतन" विधि के रूप में नाम दिया गया है जिसका अर्थ यह है कि जब भी कुछ होता है तो उसे कॉल किया जाएगा। – SmallChess

संबंधित मुद्दे