react-native-iap icon indicating copy to clipboard operation
react-native-iap copied to clipboard

[iOS] EXC_BAD_ACCESS and NSInvalidArgumentException

Open Yann-prak opened this issue 1 year ago • 14 comments

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 avatar Sep 22 '23 09:09 Yann-prak

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

image

pxpeterxu avatar Sep 22 '23 19:09 pxpeterxu

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

Yann-prak avatar Sep 28 '23 07:09 Yann-prak

Looking at the pull request, this one might fix our problem @pxpeterxu #2518 @hyochan could you merge and release it when you have time ?

Yann-prak avatar Oct 05 '23 09:10 Yann-prak

Unfortunately even with the merged fix we still have the issue :(

marf avatar Nov 03 '23 17:11 marf

@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 avatar Nov 08 '23 13:11 Yann-prak

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

marf avatar Nov 08 '23 14:11 marf

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 ? :)

Yann-prak avatar Nov 08 '23 15:11 Yann-prak

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

marf avatar Nov 08 '23 15:11 marf

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 avatar Nov 08 '23 15:11 Yann-prak

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

geroale avatar Nov 08 '23 15:11 geroale

sendEvent() might cause a crash too ? Or because you need the promise to end before sending the event ?

Yann-prak avatar Nov 08 '23 15:11 Yann-prak

@Yann-prak The wrap outside the rejectPromises may have been added for the sendEvent or for hasListeners. I don't know.

geroale avatar Nov 08 '23 15:11 geroale

@Yann-prak it seems the crashes we had due to concurrency have been solved with our patch.

marf avatar Nov 11 '23 09:11 marf

Great, thanks for the follow up :)

Yann-prak avatar Nov 13 '23 12:11 Yann-prak