From 944b3c96e9b10f6f93d4b53fc62f815d259d046e Mon Sep 17 00:00:00 2001 From: Justin Espedal Date: Wed, 8 Apr 2020 11:35:34 +0900 Subject: [PATCH] iOS 13 crash when requesting iap product info http://community.stencyl.com/index.php?issue=1674.0 Xcode 11 + iOS 13 makes the purchase callback take place from a foreign thread. https://github.com/HaxeFoundation/hxcpp/blob/master/docs/ThreadsAndStacks.md https://github.com/HaxeExtension/extension-iap/issues/55 We may want to eventually update our iap implementation to the HaxeExtension one. https://github.com/HaxeExtension/extension-iap --- project/common/ExternalInterface.cpp | 51 ++++++++++++++++++++++------ project/iphone/Purchases.mm | 24 ++++++++----- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/project/common/ExternalInterface.cpp b/project/common/ExternalInterface.cpp index c34b3b6..a483756 100755 --- a/project/common/ExternalInterface.cpp +++ b/project/common/ExternalInterface.cpp @@ -31,57 +31,65 @@ DEFINE_PRIM (purchases_initialize, 0); static void purchases_restore() { - restorePurchases(); + restorePurchases(); } DEFINE_PRIM (purchases_restore, 0); static void purchases_buy(value productID) { - purchaseProduct(val_string(productID)); + purchaseProduct(val_string(productID)); } DEFINE_PRIM(purchases_buy, 1); static void purchases_requestProductInfo(value productIDcommalist) { - requestProductInfo(val_string(productIDcommalist)); + requestProductInfo(val_string(productIDcommalist)); } DEFINE_PRIM(purchases_requestProductInfo, 1); static value purchases_title(value productID) { - return alloc_string(getTitle(val_string(productID))); + value toReturn = alloc_string(getTitle(val_string(productID))); + + return toReturn; } DEFINE_PRIM(purchases_title, 1); static value purchases_desc(value productID) { - return alloc_string(getDescription(val_string(productID))); + value toReturn = alloc_string(getDescription(val_string(productID))); + + return toReturn; } DEFINE_PRIM(purchases_desc, 1); static value purchases_price(value productID) { - return alloc_string(getPrice(val_string(productID))); + value toReturn = alloc_string(getPrice(val_string(productID))); + + return toReturn; } DEFINE_PRIM(purchases_price, 1); static value purchases_canbuy() { - return alloc_bool(canPurchase()); + value toReturn = alloc_bool(canPurchase()); + + return toReturn; } DEFINE_PRIM (purchases_canbuy, 0); static void purchases_release() { - releaseInAppPurchase(); + releaseInAppPurchase(); } DEFINE_PRIM (purchases_release, 0); static value purchases_validate(value receipt,value password, value inproductionurl) { + value toReturn = alloc_bool(validateReceipt(val_string(receipt),val_string(password), val_bool(inproductionurl))); - return alloc_bool(validateReceipt(val_string(receipt),val_string(password), val_bool(inproductionurl))); - + return toReturn; } DEFINE_PRIM (purchases_validate, 3); @@ -108,11 +116,34 @@ extern "C" void sendPurchaseEvent(const char* type, const char* data) extern "C" void sendPurchaseFinishEvent(const char* type, const char* data, const char* receiptString, const char* transactionID) { + value o = alloc_empty_object(); + alloc_field(o,val_id("type"),alloc_string(type)); + alloc_field(o,val_id("data"),alloc_string(data)); + alloc_field(o,val_id("receiptString"),alloc_string(receiptString)); + alloc_field(o,val_id("transactionID"),alloc_string(transactionID)); + val_call1(purchaseEventHandle->get(), o); +} + +extern "C" void sendPurchaseEventForeign(const char* type, const char* data) +{ + int t0; + gc_set_top_of_stack(&t0, true); + value o = alloc_empty_object(); + alloc_field(o,val_id("type"),alloc_string(type)); + alloc_field(o,val_id("data"),alloc_string(data)); + val_call1(purchaseEventHandle->get(), o); + gc_set_top_of_stack(0, true); +} +extern "C" void sendPurchaseFinishEventForeign(const char* type, const char* data, const char* receiptString, const char* transactionID) +{ + int t0; + gc_set_top_of_stack(&t0, true); value o = alloc_empty_object(); alloc_field(o,val_id("type"),alloc_string(type)); alloc_field(o,val_id("data"),alloc_string(data)); alloc_field(o,val_id("receiptString"),alloc_string(receiptString)); alloc_field(o,val_id("transactionID"),alloc_string(transactionID)); val_call1(purchaseEventHandle->get(), o); + gc_set_top_of_stack(0, true); } diff --git a/project/iphone/Purchases.mm b/project/iphone/Purchases.mm index 0dc6670..cd96b3c 100755 --- a/project/iphone/Purchases.mm +++ b/project/iphone/Purchases.mm @@ -1,11 +1,19 @@ #import #import #import +#import +#include #include "Purchases.h" #include "PurchaseEvent.h" extern "C" void sendPurchaseEvent(const char* type, const char* data); extern "C" void sendPurchaseFinishEvent(const char* type, const char* data, const char* receiptString, const char* transactionID); +extern "C" void sendPurchaseEventForeign(const char* type, const char* data); +extern "C" void sendPurchaseFinishEventForeign(const char* type, const char* data, const char* receiptString, const char* transactionID); + +#define IOS_13 ([[[UIDevice currentDevice] systemVersion] compare:@"13.0" options:NSNumericSearch] != NSOrderedAscending) +#define spe(type, data) if(IOS_13) sendPurchaseEventForeign(type, data); else sendPurchaseEvent(type, data); +#define spfe(type, data, receipt, transaction) if(IOS_13) sendPurchaseFinishEventForeign(type, data, receipt, transaction); else sendPurchaseFinishEvent(type, data, receipt, transaction); @interface InAppPurchase: NSObject { @@ -44,7 +52,7 @@ - (void)initInAppPurchase { NSLog(@"Purchases initialize"); [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; - sendPurchaseEvent("started", ""); + spe("started", ""); productsRequest = nil; authorizedProducts = [[NSMutableDictionary alloc] init]; arePurchasesEnabled = NO; @@ -65,7 +73,7 @@ - (void)purchaseProduct:(NSString*)productId { if(!arePurchasesEnabled || ![SKPaymentQueue canMakePayments]) { - sendPurchaseEvent("failed", [productId UTF8String]); + spe("failed", [productId UTF8String]); return; } @@ -77,7 +85,7 @@ - (void)purchaseProduct:(NSString*)productId return; } - sendPurchaseEvent("failed", [productId UTF8String]); + spe("failed", [productId UTF8String]); } // Multiple requests can be made, they'll be added into authorized list if not already there. @@ -252,7 +260,7 @@ - (void)productsRequest:(SKProductsRequest*)request didReceiveResponse:(SKProduc [productsRequest release]; productsRequest = nil; arePurchasesEnabled = YES; - sendPurchaseEvent("productsVerified", ""); + spe("productsVerified", ""); } #pragma mark - SKPaymentTransactionObserver and Purchase helper methods @@ -272,7 +280,7 @@ - (void)finishTransaction:(SKPaymentTransaction*)transaction wasSuccessful:(BOOL return; } - sendPurchaseFinishEvent("success", [transaction.payment.productIdentifier UTF8String],[jsonObjectString UTF8String],[transaction.transactionIdentifier UTF8String]); + spfe("success", [transaction.payment.productIdentifier UTF8String],[jsonObjectString UTF8String],[transaction.transactionIdentifier UTF8String]); } else @@ -282,7 +290,7 @@ - (void)finishTransaction:(SKPaymentTransaction*)transaction wasSuccessful:(BOOL { NSLog(@"Transaction error: %@", transaction.error.localizedDescription); } - sendPurchaseEvent("failed", [transaction.payment.productIdentifier UTF8String]); + spe("failed", [transaction.payment.productIdentifier UTF8String]); } [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; @@ -306,7 +314,7 @@ - (void)restoreTransaction:(SKPaymentTransaction*)transaction return; } - sendPurchaseFinishEvent("restore", [transaction.payment.productIdentifier UTF8String],[jsonObjectString UTF8String],[transaction.transactionIdentifier UTF8String]); + spfe("restore", [transaction.payment.productIdentifier UTF8String],[jsonObjectString UTF8String],[transaction.transactionIdentifier UTF8String]); //sendPurchaseEvent("restore", [transaction.originalTransaction.payment.productIdentifier UTF8String]); //[self finishTransaction:transaction wasSuccessful:YES]; @@ -340,7 +348,7 @@ - (void)failedTransaction:(SKPaymentTransaction*)transaction else { - sendPurchaseEvent("cancel", [transaction.payment.productIdentifier UTF8String]); + spe("cancel", [transaction.payment.productIdentifier UTF8String]); [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } }