Lessons Learned from Debugging React Native PassKit Wallet

React Native PassKit Wallet is a handy library for integrating your app with Apple Wallet. The library is simple to use. You send PassKit data to a function that runs validation and adds it to the Wallet. But what if the function doesn’t add it to the Wallet — and it doesn’t give you a clear indication of what went wrong?

Then you’d be in the same predicament I was in last week. If you find yourself spinning your wheels trying to figure out the fix, this post may help you.

Read the README

Let’s start by viewing the docs for the React Native PassKit Wallet. The following function should prompt the user to add the Pass to the Wallet.


PassKit.addPass(base64EncodedPass: string): Promise<void>

The addPass function takes in a base64 encoded string and returns a Promise.

Add log lines

Seems simple enough, I thought. I’ll add a then and a catch handler to the function and see what path the code takes.


PassKit.addPass(base64EncodedPass)
  .then( _ => {
    console.log("success");
  })
  .catch( _ => {
    console.log("error");
  });

I ran the code, and neither of the log lines was showing in the console. Nor were any other errors showing. I thought that was weird, so I put a log line directly above the addPass call. This log did show in the console, confirming that addPass must have been called. But why isn’t it returning anything?

Verify the Data

At this point, I was convinced the issue must be the data I was passing in because that is the only variable that is going into this black box that could make a difference in the output.

My first theory was the Pass data might be getting mangled up during network transport. To verify this, I logged the base64EncodedPass variable to the console. This resulted in a large string. I saved it to a file and then did research on what I should expect to see when I decode this string.

Turns out a Pass file is a compressed file. So I convert the base64EncodedPass string to a zip file.


$ cat base64EncodedPass.txt | base64 --decode > base64EncodedPass.zip

Then, I extracted the contents from the archive and inspected the file tree.


$ unzip -d base64EncodedPass base64EncodedPass.zip
$ tree base64EncodedPass
base64EncodedPass
├── icon.png
├── [email protected]
├── logo.png
├── [email protected]
├── manifest.json
├── pass.json
├── signature
├── thumbnail.png
└── [email protected]

These are the correct files I was hoping to see. So, why does addPass still not work?

Take to Xcode

I then thought maybe the issue was caused by the iOS simulator. So, I fired up Xcode to start debugging the issue on my iPhone. This is where I found the next clue.

When I triggered the addPass function again, I noticed a new error in the Xcode console.

[Presentation] Attempt to present <PKAddPassesViewController: 0x12e852240> on <UIViewController: 0x129f285d0> (from <UIViewController: 0x129f285d0>) which is already presenting <RCTModalHostViewController: 0x129e2b870>.

After pasting this error into a Google search, it became clear to me that addPass was trying to open up a Modal. However, the problem was that I already had a Modal open to show a button to prompt the user to add the Pass to their wallet!

I tested my next theory by moving the addPass button out of the Modal, and, sure enough, the addPass function worked!

Lessons Learned from Debugging React Native PassKit Wallet

I believe there are two lessons learned after going through the process of debugging the React Native PassKit Wallet.

  1. If you are using React Native libraries that open up Modals, be sure you are closing or queuing up your Modals appropriately. Two modals can not be shown at the same time.
  2. Be sure to check the logs in Xcode when you encounter weird behavior with native function calls! In this case, it was clear what the problem was after running the app on my iPhone via Xcode vs. just the Simulator with the bundler.