purchases-ios
purchases-ios copied to clipboard
Crash in CustomerInfoManager
- [x] I have updated Purchases SDK to the latest version
- [x] I have read the Contribution Guidelines
- [x] I have searched the Community
- [x] I have read docs.revenuecat.com
- [x] I have searched for existing Github issues
Describe the bug Just now received one more crash in RevenueCat lib
- Environment
- Platform: iOS
- SDK version: 4.10.2
- StoreKit 2 (enabled with
useStoreKit2IfEnabled) (Y/N): Y - OS version: iOS 15.6.1
- Xcode version: 13.4.1
- idk, its first crash here
-
- App crashed on start, on becoming active. I think, after update to new version.
- Stack trace:
Crashed: com.apple.NSURLSession-delegate
0 CookingRecipes 0x217cc4 closure #1 in CustomerInfoManager.sendUpdateIfChanged(customerInfo:) + 4330765508 (<compiler-generated>:4330765508)
1 CookingRecipes 0x217748 CustomerInfoManager.cache(customerInfo:appUserID:) + 28 (Lock.swift:28)
2 CookingRecipes 0x215268 closure #1 in CustomerInfoManager.fetchAndCacheCustomerInfo(appUserID:isAppBackgrounded:completion:) + 53 (Logger.swift:53)
3 CookingRecipes 0x259230 closure #2 in GetCustomerInfoOperation.getCustomerInfo(completion:) + 35 (CustomerInfoResponseHandler.swift:35)
4 CookingRecipes 0x26767c specialized closure #1 in HTTPClient.Request.init<A>(httpRequest:headers:completionHandler:) + 4331091580
5 CookingRecipes 0x269244 partial apply for specialized closure #1 in HTTPClient.Request.init<A>(httpRequest:headers:completionHandler:) + 4331098692 (<compiler-generated>:4331098692)
6 CookingRecipes 0x24d030 HTTPClient.handle(urlResponse:request:urlRequest:data:error:) + 237 (HTTPClient.swift:237)
7 CookingRecipes 0x24e340 partial apply for closure #1 in HTTPClient.start(request:) + 4330988352 (<compiler-generated>:4330988352)
8 CookingRecipes 0x24d9d0 thunk for @escaping @callee_guaranteed (@guaranteed Data?, @guaranteed NSURLResponse?, @guaranteed Error?) ->
👀 SDKONCALL-114 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!
Hi @anivaros, thanks for reporting!
Could you share a snippet for the code that handles the delegate call? It might help us understand what's going on.
Hi! I don't use delegate, but async methods:
On app start
Task {
if let currentOffering = try await Purchases.shared.offerings().current {
return currentOffering.availablePackages.map(\.storeProduct)
}
}
and
Task { @MainActor in
try await Purchases.shared.customerInfo()
}
and then
let customerInfoUpdatesTask = Task {
for await customerInfo in Purchases.shared.customerInfoStream {
subscriptionController.handle(update: customerInfo)
}
}
Any chance that subscriptionController might be getting dereferenced by the time the stream sends another update?
We're still digging on our side to find potential memory issues that could cause this, but that's one thing that would render the stack trace you're seeing
No, it property of singleton, so can't be dereferenced
Thanks for the report! I'm trying to reproduce the issue.
Could you share more of your setup? Is your app SwiftUI? Where do you have these Tasks?
Nothing special. Yes, SwiftUI.
I think, I can just share my StoreService and StoreSubscriptionController code: Archive.zip
StoreService inits in AppDelegate's didFinishLaunchingWithOptions
SwiftUI uses StoreSubscriptionController's @Published properties and methods.
Btw, crash appeared in production only once for now
Could you try compiling your project with SWIFT_STRICT_CONCURRENCY=targeted?
I suspect you have some thread-safety issues which that flag would uncover (specifically in StoreService.setupListenerTasks)
Also another quick question: what's the lifetime of your StoreService?
Could you try compiling your project
Yes, I'll try
Also another quick question: what's the lifetime of your StoreService?
Quick answer - whole app lifetime, it lives in app's service locator that property of AppDelegate
No errors, all ok
I've noticed that StoreService is class, but not actor. Maybe that's is problem, idk..
Now I think it should be actor 🤔
Which is the line Lock.swift:28? we're experimenting a crash and this line appears on stack trace after SystemInfo.swift:119. I think that apple hasn't got this filename in its framework (I mean, the crash reports a queue with apple identifier), it isn't?
I don't know the line because the file has some precompiled conditionals and comments. Seems to be related to revenuecat but I want to confirm it. Maybe the relation between systemInfo and lock could help me. If someone could tell me both, I'll love it.
Thanks in advance
Edit: separate issue, moved to #2198
Yesterday received this crash again, crashed after ~5 second after first app launch. SDK version: 4.15.2 OS version: iOS 16.2.0
@swrobel thanks for that stack trace. That seems like a different problem, would you mind filing issue for that? Swift is crashing when creating the string concatenation for the cache key:
"""
\(configuration.appUserID)-\(postData.isRestore)-\(postData.receiptData.asFetchToken)
-\(postData.productData?.cacheKey ?? "")
-\(postData.presentedOfferingIdentifier ?? "")-\(postData.observerMode)
-\(postData.subscriberAttributesByKey?.debugDescription ?? "")"
"""
Something tells me there might be bad data from the app user ID coming from JavaScript.
@anivaros Your stack trace matches the original one. We can't tell exactly what line is crashing there unfortunately. If you can reproduce this with a debugger attached, any chance you could let us know which line in that method is triggering the crash?
Thanks.
@anivaros could you update the SDK and let us know if you're still seeing this crash?
@NachoSoto yes, of course I will update the SDK, but I only had two crashes in half a year, so it will be hard to check if the problem persists 🙂
Oh that's good to know, so it seems like an extremely rare crash. I'll go ahead and close this but feel free to reopen if you see the issue again.
I saw it twice last week.
@jesus-mg-ios could you share a stack trace and which device it's affecting?
Yes. Here it goes.
Devices:
iOS 16.1.1, iPhone 12 Pro Max
iOS 16.1.2 iPhone 12
iOS 16.0.2 iPhone 14 Pro Max
iOS 16.0.3 iPhone 11 Pro
Stack trace 1:
Crashed: com.apple.main-thread
0 Product 0x11f45c closure #1 in CustomerInfoManager.sendUpdateIfChanged(customerInfo:) + 184716
1 Product 0x11d910 CustomerInfoManager.sendCachedCustomerInfoIfAvailable(appUserID:) + 177728
2 Product 0x1c0af0 closure #1 in Purchases.init(appUserID:requestFetcher:receiptFetcher:attributionFetcher:attributionPoster:backend:paymentQueueWrapper:userDefaults:notificationCenter:systemInfo:offeringsFactory:deviceCache:identityManager:subscriberAttributes:operationDispatcher:customerInfoManager:productsManager:offeringsManager:purchasesOrchestrator:trialOrIntroPriceEligibilityChecker:) + 845856
3 Product 0x161eac closure #1 in SystemInfo.isApplicationBackgrounded(completion:) + 457692
4 Product 0xd650 thunk for @escaping @callee_guaranteed () -> () + 4369126992 (<compiler-generated>:4369126992)
5 libdispatch.dylib 0x24b4 _dispatch_call_block_and_release + 32
6 libdispatch.dylib 0x3fdc _dispatch_client_callout + 20
7 libdispatch.dylib 0x127f4 _dispatch_main_queue_drain + 928
8 libdispatch.dylib 0x12444 _dispatch_main_queue_callback_4CF + 44
9 CoreFoundation 0x9aa08 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
10 CoreFoundation 0x7c368 __CFRunLoopRun + 2036
11 CoreFoundation 0x811e4 CFRunLoopRunSpecific + 612
12 Foundation 0x41818 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212
13 Foundation 0x416ac -[NSRunLoop(NSRunLoop) run] + 64
14 libxpc.dylib 0x19c94 _xpc_objc_main + 496
15 libxpc.dylib 0x1bdf4 xpc_main + 156
16 Foundation 0x88fcc +[NSXPCListener serviceListener] + 310
17 PlugInKit 0x19f4c pkSafeDataFromRelativeURL + 19356
18 PlugInKit 0x6734 __PLUGINKIT_CALLING_OUT_TO_CLIENT_SUBSYSTEM_FOR_INIT__ + 656
19 PlugInKit 0x5a48 pklog_get_persona_type_and_name + 188
20 ExtensionFoundation 0x215a8 EXExtensionMain + 252
21 Foundation 0xcd3dc NSExtensionMain + 240
22 ??? 0x1b0811948 (Missing)
Stack trace 2: Very similar but, with 21 lines (This happens on 9 devices)
Crashed: com.apple.main-thread
0 Product 0x11f768 closure #1 in CustomerInfoManager.sendUpdateIfChanged(customerInfo:) + 184744
1 Product 0x11dc1c CustomerInfoManager.sendCachedCustomerInfoIfAvailable(appUserID:) + 177756
2 Product 0x1c0dfc closure #1 in Purchases.init(appUserID:requestFetcher:receiptFetcher:attributionFetcher:attributionPoster:backend:paymentQueueWrapper:userDefaults:notificationCenter:systemInfo:offeringsFactory:deviceCache:identityManager:subscriberAttributes:operationDispatcher:customerInfoManager:productsManager:offeringsManager:purchasesOrchestrator:trialOrIntroPriceEligibilityChecker:) + 845884
3 Product 0x1621b8 closure #1 in SystemInfo.isApplicationBackgrounded(completion:) + 457720
4 Product 0xe838 thunk for @escaping @callee_guaranteed () -> () + 4295616568 (<compiler-generated>:4295616568)
5 libdispatch.dylib 0x63194 _dispatch_call_block_and_release + 24
6 libdispatch.dylib 0x64198 _dispatch_client_callout + 16
7 libdispatch.dylib 0x454a8 _dispatch_main_queue_callback_4CF$VARIANT$armv81 + 908
8 CoreFoundation 0x4d7e8 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
9 CoreFoundation 0xb0e8 __CFRunLoopRun + 2528
10 CoreFoundation 0x1dd7c CFRunLoopRunSpecific + 572
11 Foundation 0x1800c -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 232
12 Foundation 0x18780 -[NSRunLoop(NSRunLoop) run] + 88
13 libxpc.dylib 0x158e8 _xpc_objc_main + 532
14 libxpc.dylib 0x17aa0 xpc_main + 152
15 Foundation 0x5e28c -[NSString componentsSeparatedByCharactersInSet:] + 290
16 PlugInKit 0x1cd04 __PLUGINKIT_HANDING_CONTROL_TO_MAIN_SERVICE_LISTENER__ + 44108
17 PlugInKit 0x7254 __PLUGINKIT_CALLING_OUT_TO_CLIENT_SUBSYSTEM_FOR_INIT__ + 704
18 PlugInKit 0x62b8 pklog_get_persona_type_and_name + 192
19 ExtensionFoundation 0x5574 EXExtensionMain + 296
20 Foundation 0xbd0c8 NSExtensionMain + 212
21 ??? 0x10126c190 (Missing)
Thanks for providing those! That's helpful. Different trace but ultimately crashing on the same method. Do you happen to have debug logs from before the crash?
No, unfortunately, I couldn't reproduce it on my test devices ...
I think I just saw the issue. There's a small thread-safety issue in CustomerInfoManager.
I was able to reproduce this:
DispatchQueue.concurrentPerform(iterations: 100) { _ in
let userID = UUID().uuidString
let info = try! CustomerInfo(data: [
"request_date": "2019-08-16T10:30:42Z",
"subscriber": [
"original_app_user_id": userID,
"first_seen": "2019-06-17T16:05:33Z",
"subscriptions": [:],
"other_purchases": [:]
]])
self.customerInfoManager.cache(customerInfo: info, appUserID: userID)
}
#2224 and #2231 will likely fix this. Thanks everyone for providing those useful stack traces.
This issue has been automatically locked due to no recent activity after it was closed. Please open a new issue for related reports.