Urgent: PaymentQueueController crash in IOS 12
Platform
- iOS 12
In app purchase type
- Auto-Renewable Subscription
Environment
- Production
Version
0.13.2
Issue summary
Sometime application crashes on restore and startPayment. Repoduced on IOS 12. Looks like
unowned let paymentQueue: PaymentQueue in PaymentQueueController.swift deallocated when we trying to read it.
Report
#0. Crashed: com.apple.main-thread
0 libsystem_kernel.dylib 0x1c7c83104 __pthread_kill + 8
1 libsystem_pthread.dylib 0x1c7cff020 pthread_kill$VARIANT$mp + 380
2 libsystem_c.dylib 0x1c7bdad78 abort + 140
3 libswiftCore.dylib 0x10139d9d0 swift_vasprintf(char**, char const*, char*) + 54
4 libswiftCore.dylib 0x10139db0c swift::swift_abortRetainUnowned(void const*) + 32
5 libswiftCore.dylib 0x1013d7b2c swift_unknownUnownedTakeStrong + 70
6 SwiftyStoreKit 0x100fca854 PaymentQueueController.startPayment(:) (PaymentQueueController.swift:131)
7 SwiftyStoreKit 0x100fd7cd4 closure #1 in SwiftyStoreKit.purchaseProduct(:quantity:atomically:applicationUsername:simulatesAskToBuyInSandbox:completion:) (
#Crashed: com.apple.main-thread
0 libsystem_kernel.dylib 0x1aeb1f104 __pthread_kill + 8
1 libsystem_pthread.dylib 0x1aeb9b020 pthread_kill$VARIANT$mp + 380
2 libsystem_c.dylib 0x1aea76d78 abort + 140
3 libswiftCore.dylib 0x1017399d0 swift_vasprintf(char**, char const*, char*) + 54
4 libswiftCore.dylib 0x101739b0c swift::swift_abortRetainUnowned(void const*) + 32
5 libswiftCore.dylib 0x101773b2c swift_unknownUnownedTakeStrong + 70
6 SwiftyStoreKit 0x101374e98 static SwiftyStoreKit.restorePurchases(atomically:applicationUsername:completion:) (PaymentQueueController.swift:143)
7 MyApplication 0x1009ad060 protocol witness for Store.restorePurchases(completion:) in conformance SwiftyStoreAdapter (
I'm getting this as well:
Crashed: com.apple.main-thread SIGABRT ABORT 0x0000000185ce20dc
0 libsystem_kernel.dylib 0x185ce20dc __pthread_kill + 8
1 libsystem_pthread.dylib 0x185d5b094 pthread_kill$VARIANT$mp + 380
2 libsystem_c.dylib 0x185c3bea8 abort + 140
3 libswiftCore.dylib 0x1b44652a8 swift_vasprintf(char**, char const*, char*) + 54
4 libswiftCore.dylib 0x1b44653e4 swift::swift_abortRetainUnowned(void const*) + 32
5 libswiftCore.dylib 0x1b44aa7a8 swift_unknownObjectUnownedTakeStrong + 70
6 SwiftyStoreKit 0x105c975e4 PaymentQueueController.startPayment(_:) + 132 (PaymentQueueController.swift:132)
Some context:
-I've been using this pod for forever, never had this crash
-Prior to this update I forced users to auth on app launch/login. .completeTransations() is called on every app launch, and that code didn't change.
-What DID change was I now have an "offline mode", and allow users into the app if they have previously cached credentials albeit with reduced functionality.
Now, the crash appears to be occurring in startPayment and I see the assert:
let message = "SwiftyStoreKit.completeTransactions() must be called when the app launches."
assert(completeTransactionsController.completeTransactions != nil, message)
So the only thing I can assume is that users must be trying to start payment from offline mode and somehow that's interacting with this.
Another report:
Crashed: com.apple.main-thread
0 libsystem_kernel.dylib 0x18a0b20dc __pthread_kill + 8
1 libsystem_pthread.dylib 0x18a12b094 pthread_kill$VARIANT$mp + 380
2 libsystem_c.dylib 0x18a00bea8 abort + 140
3 libswiftCore.dylib 0x1b7a5c2a8 swift_vasprintf(char**, char const*, char*) + 54
4 libswiftCore.dylib 0x1b7a5c3e4 swift::swift_abortRetainUnowned(void const*) + 32
5 libswiftCore.dylib 0x1b7aa17a8 swift_unknownObjectUnownedTakeStrong + 70
6 SwiftyStoreKit 0x101ed35e4 PaymentQueueController.startPayment(_:) + 132 (PaymentQueueController.swift:132)
7 SwiftyStoreKit 0x101ee15f4 closure #1 in SwiftyStoreKit.purchaseProduct(_:quantity:atomically:applicationUsername:simulatesAskToBuyInSandbox:completion:) + 68 (SwiftyStoreKit.swift:68)
8 SwiftyStoreKit 0x101ee56d4 partial apply for closure #1 in SwiftyStoreKit.purchaseProduct(_:quantity:atomically:applicationUsername:simulatesAskToBuyInSandbox:completion:) (<compiler-generated>)
9 SwiftyStoreKit 0x101ed90a0 partial apply for thunk for @escaping @callee_guaranteed (@guaranteed RetrieveResults) -> () (<compiler-generated>)
10 SwiftyStoreKit 0x101ed6f78 closure #1 in ProductsInfoController.retrieveProductsInfo(_:completion:) + 66 (ProductsInfoController.swift:66)
11 SwiftyStoreKit 0x101ec7258 partial apply for closure #1 in InAppProductQueryRequest.performCallback(_:) + 76 (InAppProductQueryRequest.swift:76)
12 SwiftyStoreKit 0x101ed0f34 thunk for @escaping @callee_guaranteed () -> () (<compiler-generated>)
13 libdispatch.dylib 0x189f54a38 _dispatch_call_block_and_release + 24
14 libdispatch.dylib 0x189f557d4 _dispatch_client_callout + 16
15 libdispatch.dylib 0x189f03004 _dispatch_main_queue_callback_4CF$VARIANT$mp + 1068
16 CoreFoundation 0x18a4a5ec0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
17 CoreFoundation 0x18a4a0df8 __CFRunLoopRun + 1924
18 CoreFoundation 0x18a4a0354 CFRunLoopRunSpecific + 436
19 GraphicsServices 0x18c6a079c GSEventRunModal + 104
20 UIKitCore 0x1b6913b68 UIApplicationMain + 212
21 App 0x100943968 main + 32 (UI.swift:32)
22 libdyld.dylib 0x189f668e0 start + 4
@bizz84 Sorry to bother you, but this is occurring when users try and make a purchase.
Any ideas?
I've made some more progress on this.
The root cause seems to be a thread safety issue. My network calls are on a separate queue, and when the user gets an internet connection my code checks to see if IAPs have been loaded from the store. If not, it makes a call to SwiftyStoreKit.retrieveProductsInfo
So on Thread 0: -App init -IAP Setup is called, Swifty Store kit is setup from a helper struct (complete transactions, etc etc, the retrieve products called)
Thread 2: -User gets an internet connection -Maybe products haven't loaded yet, and no cached products are available. If this happens in the right order then: -SwiftyStoreKit.retrieveProductsInfo gets called from thread 2, while thread 0 is doing the above
fileprivate func retrieveProductsInfo(_ productIds: Set<String>, completion: @escaping (RetrieveResults) -> Void) {
return productsInfoController.retrieveProductsInfo(productIds, completion: completion)
}
gets a crash, with the lowest level being:
#0 (null) in specialized _NativeDictionary.setValue(_:forKey:isUnique:) ()
So I'd suspect the problem is a lack of thread safety w/a storage dictionary.
I'm attempting to solve this with an improved model in my app, as well as wrapping all calls to SwiftyStoreKit in main thread async dispatches. If this resolves the issue I will report back.
inflightRequests should be thread safe
any news?