Please provide the following:
- SDK Version: expo-modules-core: 1.1.0
- Platforms(Android/iOS/web/all): ios
- Add the appropriate “Tag” based on what Expo library you have a question on.
I’m trying to use the Native Modules to open the “Add to Apple Wallet” flow described by Apple here: PKAddPaymentPassViewController | Apple Developer Documentation We have the entitlement required for this and have added it to our app’s entitlements.
However, when I try to present the View I get the following error:
[CoreFoundation] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM boolValue]: unrecognized
selector sent to instance 0x6000028343c0'
*** First throw call stack:
(
0 CoreFoundation 0x000000018040e7c8 __exceptionPreprocess + 172
1 libobjc.A.dylib 0x0000000180051144 objc_exception_throw + 56
2 CoreFoundation 0x000000018041d47c +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0
3 CoreFoundation 0x00000001804126c8 ___forwarding___ + 1308
4 CoreFoundation 0x0000000180414b4c _CF_forwarding_prep_0 + 92
5 PassKitUI 0x000000010b7463e4 -[PKAddPaymentPassViewController initWithRequestConfiguration:delegate:] + 116
6 reactnativepaymentpassexample 0x0000000102fe2c6c
$sSo30PKAddPaymentPassViewControllerC20requestConfiguration8delegateABSgSo0abc7RequestG0C_So0abcdE8Delegate_pSgtcfcTO + 44
7 reactnativepaymentpass<…>
This is the majority of the code I’m using on the Swift side:
import ExpoModulesCore
import PassKit
public class AlzaReactNativePaymentPassModule: Module {
var delegate = AddPaymentPassViewDelegate()
public func definition() -> ModuleDefinition {
Name("AlzaReactNativePaymentPass")
AsyncFunction("addCardToAppleWallet") { (options: AddCardToAppleWalletOptions, promise: Promise) in
guard let currentVc = appContext?.utilities?.currentViewController() else {
throw MissingViewControllerException()
}
guard let requestConfiguration = PKAddPaymentPassRequestConfiguration(encryptionScheme: .ECC_V2) else {
promise.reject("Unable to init PKAddPaymentPassRequestConfiguration", "Error")
return
}
guard let addPaymentPassViewController = PKAddPaymentPassViewController(
requestConfiguration: requestConfiguration,
delegate: self.delegate
) else {
promise.reject("Unable to init PKAddPaymentPassViewController", "Error")
return
}
currentVc.present(addPaymentPassViewController, animated: true, completion: nil)
}
}
}
internal class AddPaymentPassViewDelegate: NSObject, PKAddPaymentPassViewControllerDelegate {
func addPaymentPassViewController(_ controller: PKAddPaymentPassViewController, didFinishAdding pass: PKPaymentPass?, error: Error?) {
print("todo")
}
func addPaymentPassViewController(_ controller: PKAddPaymentPassViewController, generateRequestWithCertificateChain certificates: [Data], nonce: Data, nonceSignature: Data, completionHandler handler: @escaping (PKAddPaymentPassRequest) -> Void) {
print("todo")
}
}
internal class MissingViewControllerException: Exception {
override var reason: String {
"Could not find the current ViewController"
}
}
internal struct AddCardToAppleWalletOptions: Record {
@Field
var cardholderName: String = ""
@Field
var last4: String = ""
@Field
var paymentReferenceID: String = ""
}
From the JS side I’m calling this via the following:
const ReactNativePaymentPass = requireNativeModule(
"ReactNativePaymentPass"
);
await ReactNativePaymentPass.addCardToAppleWallet({})
I’ve tried a couple of different things to try and fix this:
- changing this to a
Function
call rather thanAsyncFunction
- appending
.runOnQueue(.main)
toAsyncFunction
- various ways of initializing and storing the
delegate
I modeled this roughly on what Expo’s doing for the document selector/delegate here:
Happy to share any extra information on how I’ve set this up!