bndkt / react-native-app-clip

Easily add an App Clip for iOS apps built with React Native
https://bndkt.com/blog/2022/react-native-app-clip
446 stars 19 forks source link

`isClip` throws about 30% of the time on TestFlight build #26

Open nahn20 opened 9 months ago

nahn20 commented 9 months ago

Reproducing

Unfortunately, I haven't been able to reproduce this in a simpler environment due to unrelated build issues, but essentially I have a barebones App.tsx file with no other imports besides from react, react-native, and react-native-app-clip in which I execute isClip() after a small delay (100ms). The app is built using eas build and published to TestFlight. isClip throws about 30% of the time, tested by continuously force quitting and reopening the app.

I have not been able to reproduce this issue in the Simulator nor in an app built and run directly from Xcode. Those environments seem to work as intended.

Issue

The isClip throwing issue itself should be relatively easy to solve for: the error is just that undefined is not a function, which I'm guessing is due to this line of code not checking for the existence of bundleIdentifier. The more concerning part is the fact that bundleIdentifier doesn't exist. I added on some additional monitoring to see the behavior of getBundleIdentifier to see if I could find some hacky workaround, but pretty consistently, the bundle identifier is defined and correct whenever isClip succeeds and spits out a meaningless object when isClip fails.

Retrying with delay doesn't resolve this issue. Bundle identifier either exists or doesn't exist a runtime, and that doesn't change.

Solutions

It seems like the nil bundle identifier is an expected edge case (given that it's of String? type), so I think it would make sense to modify relevant API types to be optional strings.

So far, I've only gotten the chance to test this on the main app and not on the app clip, so I don't know if the behavior is consistent within the app clip. There's a chance we can assume that isClip should be false in the case of a missing bundle identifier, but that's pure speculation.

I also wonder if there's any other way to determine whether JavaScript is running within an app clip or not.

nahn20 commented 8 months ago

If anyone else stumbles upon this issue, I haven't found a solution to the original getBundleIdentifier problem.

However, I did manage to differentiate between the app clip and main app by creating a separate entry point (index.clip.js instead of index.js). You can do this by modifying addBuildPhases.ts, specifically the step in which it sets the ENTRY_FILE variable. I just added | sed s/index.js/index.clip.js/ after | tail -n 1. I also took out the conditional that checks for the existence of ENTRY_FILE so that it's always overridden with index.clip.js.

This may also be a solution to the bundle size problem, but I haven't done much investigation there.

danielafcarey commented 2 months ago

I was having similar issue with getting isClip() to work but I did notice getBundleIdentifer() was more reliable, so I basically just re-created the logic for isClip() in my own code using getBundleIdentifier() and it works (but I have no idea why isClip is still broken because I used the exact same logic from the source code:

const bundleIdentifier = getBundleIdentifier();
const isClip = bundleIdentifier?.slice(bundleIdentifier.lastIndexOf(".") + 1) === 'Clip';