react-native-iap
react-native-iap copied to clipboard
[iOS] EXC_BAD_ACCESS and NSInvalidArgumentException
Description Each time I try to launch my app on the emulator, I've got a EXC_BAD_ACCESS on this line:
func add(_ aProd: SKProduct) {
debugMessage("Add new object: \(aProd.productIdentifier) \(validProducts)")
validProducts[aProd.productIdentifier] = aProd <------ EXC_BAD_ACCESS
}
Most of the time, I can print this before the app crash :
[react-native-iap] Add new object: ABO_ANN [:]
[react-native-iap] Add new object: ABO_ANN [:]
[react-native-iap] Add new object: ABO_MEN [:]
[react-native-iap] Add new object: ABO_MEN [:]
Sometimes, the add function works and then, it fails on:
func resolvePromises(forKey key: String?, value: Any?) {
let promises: [RNIapIosPromise]? = promisesByKey[key ?? ""] <------- EXC_BAD_ACCESS
if let promises = promises {
for tuple in promises {
let resolveBlck = tuple.0
resolveBlck(value)
}
promisesByKey[key ?? ""] = nil
}
}
When it fails on this line, validProducts
seems initialized ["ABO_ANN": <SKProduct: 0x6020001b48f0>, "MENSUEL_FIRST_MONTH_DISCOUNT": <SKProduct: 0x6020000053b0>, "ABO_MEN": <SKProduct: 0x60200007eb30>]
After several re-launch, eventually it works but you gotta be patient. Yesterday I've launched more than 20 times before getting it working...
Expected Behavior Everything should work. I'm not even trying to use the IAP in emulator, it is just initialized on launch.
Environment:
- react-native-iap: 12.10.8 (but also didn't work on 12.10.7)
- react-native: 0.66.5
- Platforms (iOS, Android, emulator, simulator, device): iOS 17 on an iPhone SE emulator.
To Reproduce I don't know how to reproduce it. It just stopped working since I've upgraded to 12.x 🤷♂️
Sometimes, the app just crash and I get this stack trace:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSTaggedDate objectForKey:]: unrecognized selector sent to instance 0x8000000000000000'
*** First throw call stack:
(
0 CoreFoundation 0x00000001102b428d __exceptionPreprocess + 242
1 libobjc.A.dylib 0x000000010d8aa894 objc_exception_throw + 48
2 CoreFoundation 0x00000001102c93a3 +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0
3 CoreFoundation 0x00000001102b8a20 ___forwarding___ + 1459
4 CoreFoundation 0x00000001102bac28 _CF_forwarding_prep_0 + 120
5 libswiftCore.dylib 0x00000001186ccc3c $sSD8_VariantV11removeValue6forKeyq_Sgx_tF + 172
6 libswiftCore.dylib 0x0000000118687b02 $sSDyq_Sgxcis + 194
7 newbrand_app 0x000000010112b2bd $s5RNIap0A3IosC15resolvePromises6forKey5valueySSSg_ypSgtF + 4813
8 newbrand_app 0x000000010113cb7a $s5RNIap0A3IosC15productsRequest_10didReceiveySo010SKProductsD0C_So0G8ResponseCtF + 2730
9 newbrand_app 0x000000010113ccd8 $s5RNIap0A3IosC15productsRequest_10didReceiveySo010SKProductsD0C_So0G8ResponseCtFTo + 152
10 StoreKit 0x000000011112cbc3 __27-[SKProductsRequest _start]_block_invoke_2 + 144
11 libclang_rt.asan_iossim_dynamic.dyl 0x000000010c39c91c __wrap_dispatch_async_block_invoke + 204
12 libdispatch.dylib 0x000000011136f747 _dispatch_call_block_and_release + 12
13 libdispatch.dylib 0x00000001113709f7 _dispatch_client_callout + 8
14 libdispatch.dylib 0x00000001113734eb _dispatch_queue_override_invoke + 1487
15 libdispatch.dylib 0x00000001113847f1 _dispatch_root_queue_drain + 369
16 libdispatch.dylib 0x00000001113853b5 _dispatch_worker_thread2 + 277
17 libsystem_pthread.dylib 0x0000000112c4ec0f _pthread_wqthread + 257
18 libsystem_pthread.dylib 0x0000000112c4dbbf start_wqthread + 15
)
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSTaggedDate objectForKey:]: unrecognized selector sent to instance 0x8000000000000000'
*** First throw call stack:
(
0 CoreFoundation 0x00000001102b428d __exceptionPreprocess + 242
1 libobjc.A.dylib 0x000000010d8aa894 objc_exception_throw + 48
2 CoreFoundation 0x00000001102c93a3 +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0
3 CoreFoundation 0x00000001102b8a20 ___forwarding___ + 1459
4 CoreFoundation 0x00000001102bac28 _CF_forwarding_prep_0 + 120
5 libswiftCore.dylib 0x00000001186ccc3c $sSD8_VariantV11removeValue6forKeyq_Sgx_tF + 172
6 libswiftCore.dylib 0x0000000118687b02 $sSDyq_Sgxcis + 194
7 newbrand_app 0x000000010112b2bd $s5RNIap0A3IosC15resolvePromises6forKey5valueySSSg_ypSgtF + 4813
8 newbrand_app 0x000000010113cb7a $s5RNIap0A3IosC15productsRequest_10didReceiveySo010SKProductsD0C_So0G8ResponseCtF + 2730
9 newbrand_app 0x000000010113ccd8 $s5RNIap0A3IosC15productsRequest_10didReceiveySo010SKProductsD0C_So0G8ResponseCtFTo + 152
10 StoreKit 0x000000011112cbc3 __27-[SKProductsRequest _start]_block_invoke_2 + 144
11 libclang_rt.asan_iossim_dynamic.dyl 0x000000010c39c91c __wrap_dispatch_async_block_invoke + 204
12 libdispatch.dylib 0x000000011136f747 _dispatch_call_block_and_release + 12
13 libdispatch.dylib 0x00000001113709f7 _dispatch_client_callout + 8
14 libdispatch.dylib 0x00000001113734eb _dispatch_queue_override_invoke + 1487
15 libdispatch.dylib 0x00000001113847f1 _dispatch_root_queue_drain + 369
16 libdispatch.dylib 0x00000001113853b5 _dispatch_worker_thread2 + 277
17 libsystem_pthread.dylib 0x0000000112c4ec0f _pthread_wqthread + 257
18 libsystem_pthread.dylib 0x0000000112c4dbbf start_wqthread + 15
)
libc++abi: libc++abi: terminating due to uncaught exception of type NSExceptionterminating due to uncaught exception of type NSException
Or this stack trace :
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString count]: unrecognized selector sent to instance 0x8000000000000000'
*** First throw call stack:
(
0 CoreFoundation 0x000000011053928d __exceptionPreprocess + 242
1 libobjc.A.dylib 0x000000010db2f894 objc_exception_throw + 48
2 CoreFoundation 0x000000011054e3a3 +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0
3 CoreFoundation 0x000000011053da20 ___forwarding___ + 1459
4 CoreFoundation 0x000000011053fc28 _CF_forwarding_prep_0 + 120
5 libswiftCore.dylib 0x000000011894e40c $sSD8_VariantV8setValue_6forKeyyq_n_xtF + 108
6 libswiftCore.dylib 0x000000011890cb58 $sSDyq_Sgxcis + 280
7 newbrand_app 0x00000001013c24bb $s5RNIap0A3IosC3addyySo9SKProductCF + 1979
8 newbrand_app 0x00000001013c16a0 $s5RNIap0A3IosC15productsRequest_10didReceiveySo010SKProductsD0C_So0G8ResponseCtF + 1488
9 newbrand_app 0x00000001013c1cd8 $s5RNIap0A3IosC15productsRequest_10didReceiveySo010SKProductsD0C_So0G8ResponseCtFTo + 152
10 StoreKit 0x00000001113b1bc3 __27-[SKProductsRequest _start]_block_invoke_2 + 144
11 libclang_rt.asan_iossim_dynamic.dyl 0x000000010c62191c __wrap_dispatch_async_block_invoke + 204
12 libdispatch.dylib 0x00000001115f4747 _dispatch_call_block_and_release + 12
13 libdispatch.dylib 0x00000001115f59f7 _dispatch_client_callout + 8
14 libdispatch.dylib 0x00000001115f84eb _dispatch_queue_override_invoke + 1487
15 libdispatch.dylib 0x00000001116097f1 _dispatch_root_queue_drain + 369
16 libdispatch.dylib 0x000000011160a3b5 _dispatch_worker_thread2 + 277
17 libsystem_pthread.dylib 0x0000000112ed3c0f _pthread_wqthread + 257
18 libsystem_pthread.dylib 0x0000000112ed2bbf start_wqthread + 15
)
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString count]: unrecognized selector sent to instance 0x8000000000000000'
*** First throw call stack:
(
0 CoreFoundation 0x000000011053928d __exceptionPreprocess + 242
1 libobjc.A.dylib 0x000000010db2f894 objc_exception_throw + 48
2 CoreFoundation 0x000000011054e3a3 +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0
3 CoreFoundation 0x000000011053da20 ___forwarding___ + 1459
4 CoreFoundation 0x000000011053fc28 _CF_forwarding_prep_0 + 120
5 libswiftCore.dylib 0x000000011894e40c $sSD8_VariantV8setValue_6forKeyyq_n_xtF + 108
6 libswiftCore.dylib 0x000000011890cb58 $sSDyq_Sgxcis + 280
7 newbrand_app 0x00000001013c24bb $s5RNIap0A3IosC3addyySo9SKProductCF + 1979
8 newbrand_app 0x00000001013c16a0 $s5RNIap0A3IosC15productsRequest_10didReceiveySo010SKProductsD0C_So0G8ResponseCtF + 1488
9 newbrand_app 0x00000001013c1cd8 $s5RNIap0A3IosC15productsRequest_10didReceiveySo010SKProductsD0C_So0G8ResponseCtFTo + 152
10 StoreKit 0x00000001113b1bc3 __27-[SKProductsRequest _start]_block_invoke_2 + 144
11 libclang_rt.asan_iossim_dynamic.dyl 0x000000010c62191c __wrap_dispatch_async_block_invoke + 204
12 libdispatch.dylib 0x00000001115f4747 _dispatch_call_block_and_release + 12
13 libdispatch.dylib 0x00000001115f59f7 _dispatch_client_callout + 8
14 libdispatch.dylib 0x00000001115f84eb _dispatch_queue_override_invoke + 1487
15 libdispatch.dylib 0x00000001116097f1 _dispatch_root_queue_drain + 369
16 libdispatch.dylib 0x000000011160a3b5 _dispatch_worker_thread2 + 277
17 libsystem_pthread.dylib 0x0000000112ed3c0f _pthread_wqthread + 257
18 libsystem_pthread.dylib 0x0000000112ed2bbf start_wqthread + 15
)
libc++abi: libc++abi: terminating due to uncaught exception of type NSExceptionterminating due to uncaught exception of type NSException
@Yann-prak we're getting this too, at least in development mode. We also have production app error tracking, and it seems like it also happens there, but a lot more rarely (we've only see single-digit times in the last week amount 10000s of users)
Do you know if you've run into this only while running on iOS Simulator, or also in production/on a device? I'm wondering if it might be something with how the debugger interacts with it.
Here's our Bugsnag report for one of the ~10 crashes caused by this over the past week:
EXC_BAD_ACCESS
specialized Dictionary._Variant.isUniquelyReferenced()
Attempted to dereference garbage pointer 0x8000000000000008.
I mostly get it on iOS Simulator. When I try my app on a real device, there is no problem. I don't know what to do right now and it's very annoying cause I can't test on an iOS Simulator. It took me like 20 relaunch to get it working...
Looking at the pull request, this one might fix our problem @pxpeterxu #2518 @hyochan could you merge and release it when you have time ?
Unfortunately even with the merged fix we still have the issue :(
@marf I've found that the fix was only on the RNIapIosSk2.swift
file. If you are still using the STOREKIT1 option like I do, the error occurs on the file RNIapIos.swift
. You have to apply the same fix applied in the commit. I'm not a swift guy so I don't really know how to fix the thread safety for Storekit 1 😕
@Yann-prak we tried to create a patch for RNIapIos.swift creating a single queue for the resolve, reject and addPromise function you can give it a try here:
https://github.com/Spicy-Sparks/react-native-iap
We will test it in production and check if the issue still persist.
Oh nice ! You were quicker than me. I was trying to reproduce the same patch with a ProductStore as #2518.
I looked into your patch and I saw that you didn't modify the resolvePromises
:
func resolvePromises(forKey key: String?, value: Any?) {
let promises: [RNIapIosPromise]? = promisesByKey[key ?? ""]
if let promises = promises {
for tuple in promises {
let resolveBlck = tuple.0
resolveBlck(value)
}
promisesByKey[key ?? ""] = nil <--- EXC_BAD_ACCESS
}
}
Did you experience this kind of behavior ? If yes, did your patch fixed it ? :)
Oh nice ! You were quicker than me. I was trying to reproduce the same patch with a ProductStore as #2518.
I looked into your patch and I saw that you didn't modify the
resolvePromises
:func resolvePromises(forKey key: String?, value: Any?) { let promises: [RNIapIosPromise]? = promisesByKey[key ?? ""] if let promises = promises { for tuple in promises { let resolveBlck = tuple.0 resolveBlck(value) } promisesByKey[key ?? ""] = nil <--- EXC_BAD_ACCESS } }
Did you experience this kind of behavior ? If yes, did your patch fixed it ? :)
Yeah if it does not work best way is to create a ProductStore like the one in sk2.
We wrapped all the resolvePromises, reject and addPromises with a queue like it has been done for the reject initially to hopefully solve the issue. If it still persist we will try to change it :)
If you have ideas also let us know :)
Yes ! That's a good idea, is it possible to use myQueue.sync
in the function [resolve/reject]Promises
instead of using at each call ? I'm not good at swift so it might be silly.
Like so:
func resolvePromises(forKey key: String?, value: Any?) {
myQueue.sync(execute: { [self] in
let promises: [RNIapIosPromise]? = promisesByKey[key ?? ""]
if let promises = promises {
for tuple in promises {
let resolveBlck = tuple.0
resolveBlck(value)
}
promisesByKey[key ?? ""] = nil
}
})
}
Nevertheless, there is two race condition crash in RNIapIos. With the promises (fixed by your patch) and with the validProducts (fixed with a ProductStore).
@Yann-prak Putting it inside resolvePromises
and rejectPromises
is theoretically better but it causes another issue.
There are some points where you have to wrap also hasListeners
, for example, in the myQueue.sync
, (such as this part https://github.com/dooboolab-community/react-native-iap/blob/e294165527c3785978b698fb03b581ce3ac95ac5/ios/RNIapIos.swift#L414C21-L414C21). This is the reason why myQueue.sync
was added outside of the resolvePromises
and rejectPromises
methods.
It is not possible to put myQueue.sync
both inside the methods and outside (where needed) because nesting them multiple times causes a crash (the dispatch is called from the same thread).
This is why we opted to keep everything outside directly.
sendEvent()
might cause a crash too ? Or because you need the promise to end before sending the event ?
@Yann-prak The wrap outside the rejectPromises
may have been added for the sendEvent
or for hasListeners
. I don't know.
@Yann-prak it seems the crashes we had due to concurrency have been solved with our patch.
Great, thanks for the follow up :)