Expo Module API: iOS `unrecognized selector sent to instance` when trying to use PKAddPaymentPassViewController with delegate

Please provide the following:

  1. SDK Version: expo-modules-core: 1.1.0
  2. Platforms(Android/iOS/web/all): ios
  3. 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 than AsyncFunction
  • appending .runOnQueue(.main) to AsyncFunction
  • 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!

Also, if it’s helpful, I modeled this fairly closely on this other RN package here that’s not using Expo Modules. Not sure if there’s any tips for converting a traditional bridge like this: react-native-payment-pass/PaymentPass.swift at master · aakashsigdel/react-native-payment-pass · GitHub

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.