Overview
react-native-knotapi 3.0.0 is a native TurboModule: React Native’s codegen generates its
interface, and it no longer relies on React Native’s legacy-module interop layer. The JS API you
call (Knot, addKnotListener, the knot:* events and the payload types) is unchanged. What
changes is the set of versions it builds on, how it is mocked in Jest, and some event behaviour
that 2.x got wrong or that differed between iOS and Android.
Requirements
The React Native 0.76 template already sets iOS 15.1 (
min_ios_version_supported),
minSdkVersion 24 and compileSdkVersion 35, and React Native’s Gradle plugin compiles every
Android module for Java 17 from 0.73 on. So an app on 0.76+ that kept the template’s values changes
nothing here.
Not supported, as in 2.x: Expo Go (the SDK ships native code), Mac Catalyst and visionOS (the
bundled KnotAPI.xcframework has only iOS device and simulator slices). Expo apps that use a
development build / prebuild are supported.
If you can’t meet these yet, see Staying on 2.x.
Upgrade steps
- On React Native below 0.76, upgrade React Native first, with the React Native Upgrade Helper. Until then, stay on 2.x.
-
Install 3.0.0:
-
iOS.
-
CocoaPods: in
ios/Podfile, useplatform :ios, min_ios_version_supported(15.1 on React Native 0.76+). If you hard-codedplatform :ios, '14.0'for 2.x on React Native 0.73–0.75, putmin_ios_version_supportedback: on 0.76+ React Native’s own pods need 15.1 too, sopod installfails with “required a higher minimum deployment target” until you do. -
Set the app target’s Minimum Deployments to iOS 15.1 or later in Xcode
(
IPHONEOS_DEPLOYMENT_TARGET). This applies to CocoaPods and SwiftPM mode alike. -
Reinstall the native dependencies. With CocoaPods (the default):
Coming from 2.2.1, the
KnotAPIpod disappears fromPodfile.lock. That’s expected: the native Knot iOS SDK has been bundled inside this package since 2.3.0. In React Native’s SwiftPM mode (React Native 0.87+, experimental), run this instead ofpod install: -
Delete Xcode’s DerivedData for the app (Xcode → Settings → Locations, or
rm -rf ~/Library/Developer/Xcode/DerivedData/<YourApp>-*), so no build product from the 2.x module is reused.
-
CocoaPods: in
-
Android. Build with JDK 17 (
JAVA_HOME, or Android Studio’s Gradle JDK setting), then clean once:If your Android host doesn’t apply the React Native Gradle plugin, see Android hosts withoutcom.facebook.react. -
Rebuild and reinstall the app (
npx react-native run-ios/run-android, or your usual build). A Metro reload isn’t enough after a native module changes. -
Check your code:
- Jest tests that mock
NativeModules.Knotapi: usereact-native-knotapi/jest. - Importing the default export (
import Knotapi from 'react-native-knotapi'): see The default export. - Reading
metaData, error codes oronExit: see Behaviour changes.
- Jest tests that mock
Code changes
Jest mocks
In 3.0.0 the module is looked up throughTurboModuleRegistry, which reads React Native’s own
NativeModules object, not the react-native export your test mocks. So a 2.x mock that returns a
new NativeModules from jest.mock('react-native', …) no longer reaches the module, and importing
react-native-knotapi throws the not-linked error below. A hand-written react-native mock that
doesn’t spread jest.requireActual('react-native') fails earlier, with
TypeError: Cannot read properties of undefined (reading 'get'), because it has no
TurboModuleRegistry. A mock that instead sets
NativeModules.Knotapi on React Native’s actual module still happens to work, through React
Native’s fallback to legacy modules under Jest. Either way, mock the package itself with the mock
it ships, in a test file or your Jest setup file:
Knot.open,Knot.openWithoutDelayandKnot.closearejest.fn();Knot.getSdkVersionis ajest.fn()that returnsundefined;addKnotListeneris ajest.fn()that returns{ remove: jest.fn() };- the default export has
open,close,getSdkVersion(returnsnull),addListenerandremoveListenersasjest.fn().
expect(Knot.open).toHaveBeenCalledWith(...).
The default export
In 2.x the default export was React Native’sNativeModules.Knotapi, typed any. In 3.0.0 it is
the TurboModule itself, typed with the module’s codegen Spec: open(params), close(),
getSdkVersion(): string | null, addListener(eventName) and removeListeners(count).
Prefer the named exports, Knot and addKnotListener: they are the documented API, and
Knot.getSdkVersion() returns undefined where the module returns null. If you use the default
export, call its methods directly. Don’t spread it, copy it with Object.assign, or list its
methods with Object.keys: on the New Architecture a method only becomes an own property of the
module object once it has been read, so those give an incomplete, timing-dependent set.
Unchanged
-
Knot.open,Knot.openWithoutDelay,Knot.close,Knot.getSdkVersionandaddKnotListener. -
The event names (
knot:onSuccess,knot:onError,knot:onEvent,knot:onExit) and the exported types (KnotSuccess,KnotError,KnotEvent,KnotExit). -
The error when the native module isn’t linked. It is still thrown when the package is imported
(the
pod installline appears on iOS only):In React Native’s SwiftPM mode, runnpx react-native spminstead ofpod install.
Behaviour changes
This list is the changelog of what 3.0.0 changes at runtime, compared with 2.2.1 and 2.3.0.open() input
Errors and onExit
Event payloads
Not changed in 3.0.0
- An invalid
clientIdis stillInvalid_Client_Idon iOS andInvalid_Sessionon Android (the native SDKs differ). - Events say
product: 'transaction_link', whileopen()accepts'transactions_link'. domainUrlsis Android only, and iOS ignoresproduct(the SDK takes it from the session).
Reloads and React hosts
If the JS bundle reloads while the Knot UI is open (a dev reload, or an OTA restart such asexpo-updates’ reloadAsync), the module closes the Knot UI, and that flow sends nothing more,
including knot:onExit, to the reloaded JS. Open a new session from the reloaded app if you need
one.
One React Native host per process is supported. With more than one, the Knot UI’s events go to the
host whose Knot.open last opened it, and Knot.close() from any host closes it. The React Native SDK page has the details.
Android hosts without com.facebook.react
On the New Architecture, an Android host must build React Native’s appmodules library, which only
the React Native Gradle plugin (com.facebook.react) builds. This is React Native’s requirement, not
one this package adds. Two setups work:
- the app applies the plugin, as every React Native CLI and Expo app does;
- a library module applies it and is packaged as an AAR for a native host, for example with Callstack’s React Native Brownfield.
'PlatformConstants' could not be found (a React Native core module), before
react-native-knotapi is looked up.
For an AAR host:
- the AAR’s metadata names
com.facebook.react:hermes-android:<React Native version>, which isn’t published. Substitutecom.facebook.hermes:hermes-androidat theHERMES_VERSION_NAMEinreact-native/sdks/hermes-engine/version.properties; - the host’s
compileSdkmust be at least the React Native template’s (37 on React Native 0.87); - the native Knot Android SDK (
com.knotapi.knot:knotapi-android-sdk) reaches the host as a dependency of the AAR. A host that drops transitive dependencies must add it itself.
Staying on 2.x
If you can’t move to React Native 0.76 or iOS 15.1 yet, stay on 2.x:package.json:
latest points at 3.x and legacy-2x at the newest 2.x release. 2.x needs React
Native 0.73 or later and iOS 14.0 or later; see the
2.3.0 README on npm.