purchases-ios icon indicating copy to clipboard operation
purchases-ios copied to clipboard

Crash in CustomerInfoManager

Open anivaros opened this issue 3 years ago • 11 comments

Describe the bug Just now received one more crash in RevenueCat lib

  1. Environment
    1. Platform: iOS
    2. SDK version: 4.10.2
    3. StoreKit 2 (enabled with useStoreKit2IfEnabled) (Y/N): Y
    4. OS version: iOS 15.6.1
    5. Xcode version: 13.4.1
    6. idk, its first crash here
  2. App crashed on start, on becoming active. I think, after update to new version.
  3. 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?) ->

anivaros avatar Aug 31 '22 12:08 anivaros

👀 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!

RCGitBot avatar Aug 31 '22 12:08 RCGitBot

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.

aboedo avatar Aug 31 '22 12:08 aboedo

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)
    }
}

anivaros avatar Aug 31 '22 18:08 anivaros

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

aboedo avatar Sep 06 '22 14:09 aboedo

No, it property of singleton, so can't be dereferenced

anivaros avatar Sep 06 '22 15:09 anivaros

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?

NachoSoto avatar Sep 06 '22 23:09 NachoSoto

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

anivaros avatar Sep 07 '22 09:09 anivaros

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?

NachoSoto avatar Sep 08 '22 18:09 NachoSoto

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

anivaros avatar Sep 08 '22 18:09 anivaros

No errors, all ok

anivaros avatar Sep 08 '22 18:09 anivaros

I've noticed that StoreService is class, but not actor. Maybe that's is problem, idk.. Now I think it should be actor 🤔

anivaros avatar Sep 08 '22 18:09 anivaros

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

jesus-mg-ios avatar Oct 30 '22 21:10 jesus-mg-ios

Edit: separate issue, moved to #2198

swrobel avatar Dec 29 '22 04:12 swrobel

Yesterday received this crash again, crashed after ~5 second after first app launch. SDK version: 4.15.2 OS version: iOS 16.2.0

issue_3338251c5ca694bd01497d375a4cfc8a_crash_session_ab2edfafb4e245558afb4fd713429388_DNE_0_v2_stacktrace.txt

anivaros avatar Dec 29 '22 07:12 anivaros

@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.

NachoSoto avatar Jan 05 '23 18:01 NachoSoto

@anivaros could you update the SDK and let us know if you're still seeing this crash?

NachoSoto avatar Jan 18 '23 16:01 NachoSoto

@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 🙂

anivaros avatar Jan 18 '23 16:01 anivaros

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.

NachoSoto avatar Jan 18 '23 16:01 NachoSoto

I saw it twice last week.

jesus-mg-ios avatar Jan 18 '23 16:01 jesus-mg-ios

@jesus-mg-ios could you share a stack trace and which device it's affecting?

NachoSoto avatar Jan 18 '23 16:01 NachoSoto

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)

jesus-mg-ios avatar Jan 18 '23 16:01 jesus-mg-ios

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?

NachoSoto avatar Jan 18 '23 16:01 NachoSoto

No, unfortunately, I couldn't reproduce it on my test devices ...

jesus-mg-ios avatar Jan 18 '23 16:01 jesus-mg-ios

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)
}

NachoSoto avatar Jan 18 '23 17:01 NachoSoto

#2224 and #2231 will likely fix this. Thanks everyone for providing those useful stack traces.

NachoSoto avatar Jan 18 '23 19:01 NachoSoto

This issue has been automatically locked due to no recent activity after it was closed. Please open a new issue for related reports.

github-actions[bot] avatar Jan 26 '23 00:01 github-actions[bot]