InApp Purchase

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • InApp Purchase

    Hallo zusammen,

    ich durchforste bereits tagelang das Internet und finde keine Lösung für mein Problem. Ich habe eine App erstellt und möchte die Werbung durch einen In App Kauf entfernen lassen. Mein Problem ist, das die App manchmal abstürzt wenn ich auf den Button "BuyProduct" klicke. Ich erhalte dann die Fehlermeldung "Thread 1: EXC_BAD_ACCESS (code=1, address = 0x16e6f6980)" hinter der Zeile

    Quellcode

    1. [[SKPaymentQueue defaultQueue] addPayment:payment];
    .

    Hier mein code in der .h Datei meines PurchasedViewController:

    Quellcode

    1. ​#import <UIKit/UIKit.h>
    2. #import <StoreKit/StoreKit.h>
    3. @interface PurchasedViewController2 : UIViewController <SKPaymentTransactionObserver, SKProductsRequestDelegate>{
    4. NSTimer *myTimer;
    5. int countdown;
    6. }
    7. @property (strong, nonatomic) SKProduct *product;
    8. @property (strong, nonatomic) NSString *productID;
    9. @property (strong, nonatomic) IBOutlet UILabel *productTitle;
    10. @property (strong, nonatomic) IBOutlet UITextView *productDescription;
    11. @property (strong, nonatomic) IBOutlet UIButton *buyButton;
    12. @property (weak, nonatomic) IBOutlet UIButton *goBack;
    13. @property (weak, nonatomic) IBOutlet UILabel *labelGray;
    14. @property (weak, nonatomic) IBOutlet UILabel *labelCountdown;
    15. - (IBAction)GoBack:(id)sender;
    16. - (IBAction)BuyProduct:(id)sender;
    17. - (IBAction)Restore:(id)sender;
    18. -(void)getProductID:(UIViewController *)viewController;
    19. -(void)UnlockPurchase;
    20. -(void)update;
    21. @end
    Alles anzeigen

    hier die entsprechende .m

    Quellcode

    1. ​#import "PurchasedViewController2.h"
    2. #import "ViewController.h"
    3. @interface PurchasedViewController2 ()
    4. @property (strong, nonatomic) ViewController *homeViewController;
    5. @end
    6. @implementation PurchasedViewController2
    7. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    8. {
    9. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    10. if (self) {
    11. // Custom initialization
    12. }
    13. return self;
    14. }
    15. - (void)viewDidLoad
    16. {
    17. [super viewDidLoad];
    18. countdown = 5;
    19. _labelCountdown.text = [NSString stringWithFormat:@"%i",countdown];
    20. _goBack.enabled = FALSE;
    21. myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(update) userInfo:nil repeats:YES];
    22. // Do any additional setup after loading the view.
    23. }
    24. -(void)update{
    25. countdown--;
    26. if (countdown >=0) {
    27. _labelCountdown.text = [NSString stringWithFormat:@"%i",countdown];
    28. }
    29. if (countdown <=0) {
    30. _labelCountdown.hidden = TRUE;
    31. _labelGray.hidden = TRUE;
    32. _goBack.enabled = TRUE;
    33. [myTimer invalidate];
    34. }
    35. }
    36. - (void)didReceiveMemoryWarning
    37. {
    38. [super didReceiveMemoryWarning];
    39. // Dispose of any resources that can be recreated.
    40. }
    41. /*
    42. #pragma mark - Navigation
    43. // In a storyboard-based application, you will often want to do a little preparation before navigation
    44. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    45. {
    46. // Get the new view controller using [segue destinationViewController].
    47. // Pass the selected object to the new view controller.
    48. }
    49. */
    50. - (IBAction)GoBack:(id)sender {
    51. [self dismissViewControllerAnimated:YES completion:NULL];
    52. }
    53. - (IBAction)BuyProduct:(id)sender {
    54. SKPayment *payment = [SKPayment paymentWithProduct:_product];
    55. [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    56. [[SKPaymentQueue defaultQueue] addPayment:payment];
    57. }
    58. -(void)viewDidUnload{
    59. [[SKPaymentQueue defaultQueue]removeTransactionObserver:self];
    60. [super viewDidUnload];
    61. }
    62. - (IBAction)Restore:(id)sender {
    63. [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    64. [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
    65. }
    66. -(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue{
    67. [self UnlockPurchase];
    68. }
    69. -(void)getProductID:(ViewController *)viewController{
    70. _homeViewController = viewController;
    71. if ([SKPaymentQueue canMakePayments]) {
    72. SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:self.productID]];
    73. request.delegate = self;
    74. [request start];
    75. }
    76. else{
    77. _productDescription.text = @"Please enable in app purchase in your setting";
    78. }
    79. }
    80. #pragma mark _
    81. #pragma mark SKProductsRequestDelegate
    82. -(void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
    83. NSArray *products = response.products;
    84. if (products.count != 0) {
    85. _product = products[0];
    86. _buyButton.enabled = YES;
    87. _productTitle.text = _product.localizedTitle;
    88. _productDescription.text = _product.localizedDescription;
    89. }
    90. else{
    91. _productTitle.text = @"Product Not Found";
    92. }
    93. products = response.invalidProductIdentifiers;
    94. for (SKProduct *product in products) {
    95. NSLog(@"Product not Found: %@", product);
    96. }
    97. }
    98. -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    99. for (SKPaymentTransaction *transaction in transactions) {
    100. switch (transaction.transactionState) {
    101. case SKPaymentTransactionStatePurchased:[self UnlockPurchase];
    102. [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    103. break;
    104. case SKPaymentTransactionStateFailed:NSLog(@"Transaction Failed");
    105. [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    106. default:
    107. break;
    108. }
    109. }
    110. }
    111. -(void)UnlockPurchase{
    112. _buyButton.enabled = NO;
    113. [_buyButton setTitle:@"Purchased" forState:UIControlStateDisabled];
    114. [_homeViewController Purchased];
    115. }
    116. @end
    Alles anzeigen


    und hier der Aufruf des Views:

    Quellcode

    1. ​- (IBAction)PurchaseItem:(id)sender {
    2. _purchaseController = [[PurchasedViewController2 alloc] initWithNibName:nil bundle:nil];
    3. _purchaseController.productID =@"PS.PileUp.RemoveAds";
    4. [[SKPaymentQueue defaultQueue] addTransactionObserver:_purchaseController];
    5. [self presentViewController:_purchaseController animated:YES completion:NULL];
    6. [_purchaseController getProductID:self];
    7. }


    Vielen dank & Grüße
  • Du prüfst anscheinend nirgends, ob products (also das Array) überhaupt schon gefüllt ist. Ich vermute jetzt mal, dass der Fehler immer dann auftritt, wenn "productsRequest:(SKProductsRequest *)request didReceiveResponse" noch nicht aufgerufen wurde. Da solltest du umgehen können indem du prüfst, ob self.product != nil ist.

    Gibt es eigentlich einen Grund warum du auf product direkt zugreifst und nicht per setter?
  • Vielen dank für die Antwort ich habe jetzt folgende Methode wie folgt geändert:

    Quellcode

    1. ​- (IBAction)BuyProduct:(id)sender {
    2. NSLog(@"buyPoduct button pushed");
    3. if (self.product != nil){
    4. SKPayment *payment = [SKPayment paymentWithProduct:_product];
    5. [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    6. [[SKPaymentQueue defaultQueue] addPayment:payment];
    7. }
    8. }


    Nun taucht der Gleiche Fehler (mit anderer Adresse) in der main.m auf und zwar hinter dieser Zeile:

    Quellcode

    1. ​return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));


    Hier die ganze main. m:

    Quellcode

    1. ​#import <UIKit/UIKit.h>
    2. #import "AppDelegate.h"
    3. int main(int argc, char * argv[])
    4. {
    5. @autoreleasepool {
    6. return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    7. }
    8. }


    Grüße
  • @ioscampus
    2014-04-30 14:06:48.433 PileUp[610:60b] buyPoduct button pushed
    (lldb)

    leider kein Fehler, nur mein NSLog und dann dieses (lldb)

    @MCDan
    Ich bin davon ausgegangen das ich keine releases mehr brauche, da das von dem ARC übernommen wird.
    Ich habe auch schon versucht was mit release zu machen aber das ist in der xcode Hilfe immer durchgestrichen und verursacht Fehler :(


    Es kann durchaus sein das es was mit dem Speicher zu tun hab, mir ist aufgefallen das der Fehler wohl immer dann auftritt wenn ich das 2. mal den View aufrufe. Beim ersten mal kann ich sooft auf den Button drücken wie ich will beim 2. mal stürzt die App direkt bei dem ersten Drücken ab.
  • Ein EXC_BAD_ACCESS ist in 99,9% der Fälle ein Problem im Zusammenhang mit dem Speichermanagement, d.h. die Laufzeitumgebung greift auf ein Objekt zu welches nicht mehr existiert.

    ARC benötigt zwar kein retain/relese mehr, allerdings muss man Objekte strong referenzieren, welche man "behalten" möchte, da diese sonst von ARC "weggeräumt" werden. ;)

    Wenn ich mir diese Zeile

    Quellcode

    1. _product = products[0];

    anschaue, dann sehe ich leider keine strong Referenzierung sondern nur eine Wertzuweisung. Wenn Du nicht den Setter zu einer Property verwendest, dann weisst Du nur einen Wert zu und erhältst damit keine strong Referenz auf das referenzierte Objekt.

    Hast Du mal ein "Product > Analyze" gemacht? Dann sollte Dir die o.a. Stelle eigentlich sogar angezeigt werden.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MCDan ()

  • Und wie soll ich das ändern damit es funktioniert?

    Aber mir reicht der Wert doch eigentlich. Wenn ARC so funktioniert wie ich mir das denke löscht das alle Objekte beim verlassen des Views und erstellt sie wieder neu wenn der View erneut aufgerufen wird.

    Bei "Product > Analyze" zeigt er mir 3 Blaue Sachen an, aber nicht in den betroffenen Dateien.
  • devilcrack schrieb:

    Bei "Product > Analyze" zeigt er mir 3 Blaue Sachen an, aber nicht in den betroffenen Dateien.

    Dann fixe mal die angezeigten Stellen und teste die App erneut.

    devilcrack schrieb:

    Und wie soll ich das ändern damit es funktioniert?

    Indem Du beim Zugriff bzw. bei Wertzuweisungen an ein Property den Setter verwendest. Siehe auch "Use Accessor Methods to Get or Set Property Values" in "Programming with Objective-C > Encapsulating Data".
  • devilcrack schrieb:

    Das geht aber nicht

    Quellcode

    1. [_product setArray product[0]]


    macht der nicht

    Du willst ja auch nicht ein Property von _product, also der Klasse SKProduct setzen, sondern das Property product der eigenen Klasse. Dies würde dann natürlich so aussehen:

    Quellcode

    1. [self setProduct:[products objectAtIndex:0]];

    Alternativ kannst Du auch die Dot Syntax verwenden. Dies würde dann so aussehen:

    Quellcode

    1. self.product = [products objectAtIndex:0];


    Empfehlen würde ich in diesem Zusammenhang auch dies hier: Apps programmieren für iPhone und iPad von Klaus M. Rodewig, Clemens Wagner

  • Garantiert gibt es da mehr Informationen beim Crash.
    Prüfe bitte ob im Breakpoint Navigator etwas angelegt ist. Andernfalls unten das kleine + und "Add Exception Breakpoint" und auf Exception: All; Break: On Throw stellen.

    Du erhältst zumindest links eine Übersicht wo es kracht. Auch dass nur jeder zweite Viewaufruf betroffen ist ist interessant.
    Wo räumst du deine View wieder auf, wenn du sie schließt? Ist deine "GoBack" Methode alles?
  • devilcrack schrieb:


    Ja, ich Räume nur in der "GoBack" Methode auf, ich denke den rest macht ARC für mich.


    Was passiert mit dem gesetzem Delegate des SKProductsRequest? Du setzt das Delegate; setzt anschließend einen Request ab und gehst zurück. Wenn nun die Antwort kommt, ist ja niemand mehr da, um sie zu verarbeiten, oder?

    Ich bin nicht sicher - das ist jetzt auch eine Frage an die, die hier mehr wissen. Aber gefühlt geht das schief.
  • Das Problem könnte an addTransactionObserver: liegen. Du fügst zwar den ViewController als Observer hinzu entfernst diesen allerdings nicht. Die Methode viewDidUnload wird seit iOS 6 nicht mehr aufgerufen. Vor iOS 6 erfolgte der Aufruf auch nur, wenn die App eine Memory Warning erhalten hat und auch nur für die ViewController, welche aktuell nicht angezeigt wurden. Somit wird removeTransactionObserver: in viewDidUnload natürlich nie aufgerufen.

    Es bietet sich an als TransactionObserver ein Objekt zu verwenden, welches bereits im AppDelegate angelegt und dort als Observer zur SKPaymentQueue hinzugefügt wird. Somit ist sichergestellt, dass z.B. auch nicht komplett abgeschlossene Payment Transactions korrekt von der App verarbeitet werden können. Siehe auch "Waiting for the App Store to Process Transactions" im "In-App Purchase Programming Guide".
  • JUHUUU es klappt ich hab das hier :

    Quellcode

    1. [[SKPaymentQueue defaultQueue]removeTransactionObserver:self];


    in die GoBack Methode geschrieben und nun funktioniert es.

    Vielen Vielen Dank :)

    Wenn du mir jetzt noch sagen kannst warum meine Ads nur angezeigt werden wenn ich im Wifi bin bist du mein Held :)

    Edit: Nichtmals meine Website die ich als Impressum nutze wird angezeigt, mit der Reachability wird mir aber angezeigt das ich eine WAN Verbindung habe.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von devilcrack ()