IAP后,应用程序崩溃-仅在更新podfile之后 [英] App is crashing after an IAP - only after podfiles were updated

查看:62
本文介绍了IAP后,应用程序崩溃-仅在更新podfile之后的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在应用程序中有一个IAP设置,还有一些Cocoapods:

I have an IAP setup in an app, along with a few Cocoapods:

  - Firebase/AdMob (4.8.0):
    - Firebase/Core
    - Google-Mobile-Ads-SDK (= 7.27.0)
  - Firebase/Core (4.8.0):
    - FirebaseAnalytics (= 4.0.5)
    - FirebaseCore (= 4.0.13)
  - Firebase/Crash (4.8.0):
    - Firebase/Core
    - FirebaseCrash (= 2.0.2)
  - FirebaseAnalytics (4.0.5):
    - FirebaseCore (~> 4.0)
    - FirebaseInstanceID (~> 2.0)
    - GoogleToolboxForMac/NSData+zlib (~> 2.1)
    - nanopb (~> 0.3)
  - FirebaseCore (4.0.13):
    - GoogleToolboxForMac/NSData+zlib (~> 2.1)
  - FirebaseCrash (2.0.2):
    - FirebaseAnalytics (~> 4.0)
    - FirebaseInstanceID (~> 2.0)
    - GoogleToolboxForMac/Logger (~> 2.1)
    - GoogleToolboxForMac/NSData+zlib (~> 2.1)
    - Protobuf (~> 3.1)



IAP and all of the above frameworks are working perfect! No problems at all.

一旦我进行了Pod更新,事情就开始往南走。

pod更新后,以下是更新的版本:

After a pod update, here are the updated versions:

PODS:

  - Firebase/AdMob (4.10.1):
    - Firebase/Core
    - Google-Mobile-Ads-SDK (= 7.29.0)
  - Firebase/Core (4.10.1):
    - FirebaseAnalytics (= 4.1.0)
    - FirebaseCore (= 4.0.17)
  - Firebase/Crash (4.10.1):
    - Firebase/Core
    - FirebaseCrash (= 2.0.2)
  - FirebaseAnalytics (4.1.0):
    - FirebaseCore (~> 4.0)
    - FirebaseInstanceID (~> 2.0)
    - GoogleToolboxForMac/NSData+zlib (~> 2.1)
    - nanopb (~> 0.3)
  - FirebaseCore (4.0.17):
    - GoogleToolboxForMac/NSData+zlib (~> 2.1)
  - FirebaseCrash (2.0.2):
    - FirebaseAnalytics (~> 4.0)
    - FirebaseInstanceID (~> 2.0)
    - GoogleToolboxForMac/Logger (~> 2.1)
    - GoogleToolboxForMac/NSData+zlib (~> 2.1)
    - Protobuf (~> 3.1)

此pod更新后-我的IAP成功购买商品时100%崩溃。绝对代码没有改变。只是对上面列出的最新框架的一个pod更新。

After this pod update - my IAP crashes on a successful purchase 100% of the time. Absolutely nothing changed in code. Just a pod update to the newest frameworks listed above.

IAP完成后,我将收到以下崩溃消息(并且弹出您已准备就绪!的提示)上):

I am getting the following crash once the IAP completes (and the "You're all set!" success alert pops up):

libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

    libsystem_kernel.dylib`__pthread_kill:
    0x1859bc2e0 <+0>:  mov    x16, #0x148
    0x1859bc2e4 <+4>:  svc    #0x80
->  0x1859bc2e8 <+8>:  b.lo   0x1859bc300               ; <+32>
    0x1859bc2ec <+12>: stp    x29, x30, [sp, #-0x10]!
    0x1859bc2f0 <+16>: mov    x29, sp
    0x1859bc2f4 <+20>: bl     0x18599cbdc               ; cerror_nocancel
    0x1859bc2f8 <+24>: mov    sp, x29
    0x1859bc2fc <+28>: ldp    x29, x30, [sp], #0x10
    0x1859bc300 <+32>: ret

这是调试面板的屏幕截图: https://i.stack.imgur.com/exmsO.png

Here is a screenshot of the debug panel: https://i.stack.imgur.com/exmsO.png

[![Debug panel][1]][1]

这是Firebase崩溃报告记录的内容:

Here is what Firebase crash reporting is logging:

-[__NSCFBoolean timeIntervalSince1970]: unrecognized selector sent to instance 0x1b6f8a878

一些注意事项:

Some items to note:


  • 代码没有任何变化。

  • 可可豆在终端中进行了更新。步骤:1. CD到dir,2. $ pod update

  • 在更新Cocoapods之前,我测试了IAP-一切正常。应用程序没有崩溃。

  • 在Cocoapod更新后,我在测试IAP之前完成了项目清理。

  • 在多台设备上崩溃-(iOS 11.2。 6和11.2.1)。

  • Nothing at all changed in code.
  • Cocoapods were updated in terminal. Steps: 1. CD to dir, 2. $ pod update
  • I tested the IAP before updating Cocoapods - everything worked flawless; the app did not crash.
  • I did a project clean before testing the IAP after Cocoapod update.
  • Crashing on multiple devices - (iOS 11.2.6 and 11.2.1).

由于我仅更新了广告连播,导致此崩溃的原因是什么?

赏金更新:

我为这个问题添加了赏金,因为我现在在其他项目上经历过。我有一个旧项目,想更新Pods(Firebase / Firebase崩溃/ Google Ads)。这是我采取的确切步骤:

I have added a Bounty to this question because I am now experiencing it on other projects. I had an old project that i wanted to update the Pods (Firebase / Firebase Crash / Google Ads). Here are the exact steps I took:


  1. CD到项目目录。

  2. Pod更新。 Cocoapods根本没有给我任何错误。

在Xcode中,我运行了一个我更新了Podfile的项目...购买IAP,完成后立即崩溃。同样,在pod文件更新之前不会发生这种情况!在运行pod update之前,IAP可以正常工作。

Within Xcode, I run the project whose Podfile I updated... I go through purchasing an IAP and it crashes as soon as it's complete. Again, this does not happen before the pod file was updated! The IAP works fine until I run pod update.

对于新近损坏的项目,我删除了Podfile,Podfile.lock和Pods目录。我从一个较旧的项目中拖动了相同的文件和目录。完美运行而不会崩溃。

With the newly offended broken project, I removed Podfile, Podfile.lock, and Pods directory. I dragged the same files and directory in from an older project. Works perfect without any crashing.

此问题仅在pod更新后仍然存在。我迷路了。

This problem is persisting ONLY after pod update. I'm lost..

IAP帮助文件

  import StoreKit
import Firebase

public typealias MYProductIdentifier = String
public typealias MYProductRequestCompletionHandler = (_ success: Bool, _ products: [SKProduct]?) -> ()

// MARK: - Class

public class IAPHelper: NSObject {

    // Define properties!
    fileprivate let myProductIdentifiers: Set<MYProductIdentifier>
    fileprivate var myPurchasedProductIdentifiers = Set<MYProductIdentifier>()

    // Optional properties
    fileprivate var myProductsRequest: SKProductsRequest?
    fileprivate var myProductsRequestCompletionHandler: MYProductRequestCompletionHandler?


    // NOTIFICATION
    static let IAPTransactionInProgress = "IAPTransactionInProgress"
    static let IAPTransactionFailed = "IAPTransactionFailed"
    static let myIAPHelperPurchaseNotification = "IAPHelperPurchaseNotification" // Whenever a purchase takes place!
    static let myRestorePurchaseNotification = "myRestorePurchaseNotification" // Whenever a restore takes place!
    static let myPurchaseMadeThankYou = "myPurchaseMadeThankYou" // Whenever a first purchase takes place!


    // init!
    public init(productIDs: Set<MYProductIdentifier>) {
        myProductIdentifiers = productIDs

        // CHECK IF USER ALREADY BOUGHT! (to set the correct Defaults)
        for productIdentifier in productIDs {
            let purchased = MYConstants.nsDefaults.bool(forKey: productIdentifier)
            if purchased {
                myPurchasedProductIdentifiers.insert(productIdentifier)
                print("Already purchased! \(productIdentifier)")
            }
            else {
                print("Not yet purchased! \(productIdentifier)")
            }
        }

        super.init()

        SKPaymentQueue.default().add(self)

    }


    public func requestProducts(completionHandler: @escaping MYProductRequestCompletionHandler) {
        myProductsRequest?.cancel()
        myProductsRequestCompletionHandler = completionHandler

        myProductsRequest = SKProductsRequest(productIdentifiers: myProductIdentifiers)
        myProductsRequest?.delegate = self
        myProductsRequest?.start()
    }

    public func buyProduct(product: SKProduct) {
        let payment = SKPayment(product: product)
        SKPaymentQueue.default().add(payment)
    }

    public func isProductPurchased(productIdentifier: MYProductIdentifier) -> Bool {
        return myPurchasedProductIdentifiers.contains(productIdentifier)
    }

    public class func canMakePayment() -> Bool {
        return SKPaymentQueue.canMakePayments()
    }

    public func restorePurchases() {
        SKPaymentQueue.default().restoreCompletedTransactions()
    }

}

// MARK: - SKProductRequestsDelegate

extension IAPHelper: SKProductsRequestDelegate {

    public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        let products = response.products
        myProductsRequestCompletionHandler?(true, products)
        reset()
    }

    public func request(_ request: SKRequest, didFailWithError error: Error) {
        // Called wheneever there is an ERROR or NO PRODUCTS!
        myProductsRequestCompletionHandler?(false, nil)
        reset()
        print("ERROR \(error.localizedDescription)")
    }

    private func reset() {
        myProductsRequest = nil
        myProductsRequestCompletionHandler = nil
    }

}

// MARK: - SKPaymentTransactionObserver

extension IAPHelper: SKPaymentTransactionObserver {

    // Tells us if the payment from the user was successful. Then react accordingly!

    public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        // Check outstanding transactions and react to them.
        for transaction in transactions {
            // check what kind of transaction is happening!
            switch transaction.transactionState {
            case .purchased :
                completeTransaction(transaction: transaction)
            case .failed :
                failedTransaction(transaction: transaction)
            case .restored :
                restoreTransaction(transaction: transaction)
            case .deferred :
                showTransactionAsInProgress(deferred: true)
            case .purchasing :
                showTransactionAsInProgress(deferred: false)
            }
        }

    }

    //MARK: Payment transaction related methods
    private func showTransactionAsInProgress(deferred: Bool) {
        NotificationCenter.default.post(name: Notification.Name(IAPHelper.IAPTransactionInProgress), object: deferred)
    }


    private func completeTransaction(transaction: SKPaymentTransaction) {
        postPurchaseNotificationForIdentifier(identifier: transaction.payment.productIdentifier)
        NotificationCenter.default.post(name: NSNotification.Name(IAPHelper.myPurchaseMadeThankYou), object: nil)
        SKPaymentQueue.default().finishTransaction(transaction)
    }

    private func failedTransaction(transaction: SKPaymentTransaction) {
        // User aborts payment!!
        if transaction.error!._code != SKError.Code.paymentCancelled.rawValue {
            print("Error: \(transaction.error!.localizedDescription)")
        }

        NotificationCenter.default.post(name: Notification.Name(IAPHelper.IAPTransactionFailed), object: transaction.error)

        SKPaymentQueue.default().finishTransaction(transaction)
    }

    private func restoreTransaction(transaction: SKPaymentTransaction) {
        guard let productIdentifier = transaction.original?.payment.productIdentifier else {
            return
        }

        postRestoreNotificationForIdentifier(identifier: productIdentifier)


        SKPaymentQueue.default().finishTransaction(transaction)
    }





    private func postPurchaseNotificationForIdentifier(identifier: String?) {
        // TELL VC THAT PURCHASE WAS OR WAS NOT success.
        guard let identifier = identifier else {
            return
        }

        Analytics.logEvent("IAP_Purchase_Made", parameters: nil)


        // I believe it crashes right here.

        // NEW ==================================
        myPurchasedProductIdentifiers.insert(identifier)
        MYConstants.nsDefaults.set(true, forKey: identifier)
        MYConstants.unlockLogic(restoring: false)
        NotificationCenter.default.post(name: Notification.Name(IAPHelper.myIAPHelperPurchaseNotification), object: identifier)
        // END NEW ==============================

    }



    private func postRestoreNotificationForIdentifier(identifier: String?) {
        // TELL VC THAT PURCHASE WAS OR WAS NOT success.
        guard let identifier = identifier else {
            return
        }

        Analytics.logEvent("IAP_Restore_Made", parameters: nil)

        // NEW ==================================
        myPurchasedProductIdentifiers.insert(identifier)
        MYConstants.nsDefaults.set(true, forKey: identifier)
        print("NEW RESTORE Identifier: \(identifier)")
        MYConstants.unlockLogic(restoring: true)
        NotificationCenter.default.post(name: NSNotification.Name(IAPHelper.myRestorePurchaseNotification), object: nil)
        // END NEW ==============================

    }



}


推荐答案

我发现当 registerDefaults:用于注册用户首选项时会发生此崩溃与IAP产品标识符相同的密钥。

I found out that this crash occurs when registerDefaults: is used to register a user preference key that is the same as the IAP product identifier.

当与IAP产品ID密钥相同的NSUserDefaults密钥具有默认的prefs值注册时,总是发生此异常:

This exception always occurs when the NSUserDefaults key that is the same as the IAP product ID key is has a default prefs value registered like so:

#define kTipPurchasedIAPProductIdKey @"tipAdditional99Cents"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // Override point for customization after application launch.


  if (YES) {
      //Firebase initialization
      [FIRApp configure];
      [[FIRConfiguration sharedInstance] setLoggerLevel:FIRLoggerLevelMin];
  }

    //Register user defaults
    [[NSUserDefaults standardUserDefaults] registerDefaults:@{
                                                              kTipPurchasedIAPProductIdKey: @NO
                                                              }];
}

在这种情况下,IAP产品ID密钥(可用于验证是否iAP存在,等等)为tipAdditional99Cents。当交易返回购买/还原状态时,将发生崩溃。

In this case the IAP product ID key (that you would use to validate whether the iAP exists, etc) is tipAdditional99Cents. The crash occurs when the transaction returns as purchased/restored.

当前的解决方法是在用户默认值中注册其他密钥,而不是实际的IAP产品ID。

The current workaround is to register a different key in the user defaults instead of the actual IAP product ID.

我在其Github存储库中向Google Firebase报告了此处,他们将在下一版本(6.13.0,已修复14NOV2019)中修复该错误。

I reported it to Google Firebase on their Github repository here and they will fix the bug in the next release (6.13.0, fixed 14NOV2019).

这篇关于IAP后,应用程序崩溃-仅在更新podfile之后的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆