react-native-appstack-sdk npm package, newest first. Always integrate against the latest
stable release — appstack-cli review will tell you what you
are on.
React Native integration guide
Install, configure, and send your first events.
Added
- iOS: Added experimental React Native 0.87 Swift Package Manager support. The hand-written manifest splits the Swift bridge from the Objective-C++ TurboModule, wires React Native’s generated code and header products, and resolves the exact native Appstack SDK version from its public Swift package. CocoaPods support is unchanged.
Added
- Custom event parameters are encrypted on the device before they are sent. Values such as an email address, phone number or name are encrypted in the app rather than on arrival. Parameter names that must stay readable — among them
currency,revenueand campaign fields — are excluded, and that list is controlled by Appstack’s backend. No JavaScript change is required andsendEventcall sites are unaffected.- On iOS this requires iOS 17 or later; on iOS 15 and 16 values are encrypted server-side as before. On Android it works down to the SDK’s minimum API level 21.
- Revenue is read out of the parameters before encryption, so revenue reporting is unchanged, as are purchase
transaction_detailsand deeplink user data. - A value that cannot be encrypted is omitted from the event, and the remaining parameters are still sent.
- On Android it adds no new dependency and grows the release AAR by roughly 36 KB. On iOS it is built into the vendored framework.
- iOS: Events carry a diagnostic recording whether Apple Ads attribution was enabled and whether the AdServices token fetch is pending, succeeded or failed. The SDK sends this diagnostic automatically once token resolution completes, rather than on the next
sendEvent(native4.5.2). It has no JavaScript API. - iOS: The vendored framework ships an Apple privacy manifest declaring its required-reason API usage (native
4.5.1). No additions to the app’s ownPrivacyInfo.xcprivacyare required.
- iOS: Updated
AppstackSDK.xcframeworkto4.6.0, which brings the on-device parameter encryption above along with the parameter and attribution fixes below. - Android: Updated the native Appstack Android SDK dependency to
1.8.0, which brings the on-device parameter encryption above. - A manual
sendEvent('ASA_ATTRIBUTION')is now dropped rather than sent as a custom event. iOS4.6.0addsASA_ATTRIBUTIONto its native event enum and emits it automatically once AdServices token resolution completes, so it joinsINSTALL,FIRST_OPENandFIRST_OPEN_GUARDEDin the set the wrapper refuses to forward. It is not part of the publicEventTypeAPI on either platform.
- iOS: A
nullnested inside a parameter no longer drops the event. Null values are omitted from the payload and nulls inside arrays are preserved, matching Android. Top-levelnullandundefinedhave been stripped in JavaScript since 3.0.0, but that stripping is shallow, so a null inside an object or array still reached native, where the event was discarded. - iOS: A parameter holding a value JSON cannot represent no longer crashes the app. This covers
NaNand infinite numbers, aDate,URL,Setor class instance. Such keys are dropped individually and named in an error log, and the event is sent with its remaining parameters.NaNandInfinityare typed asnumber, so they satisfy theJsonValuetype onparameters. - iOS: A single
nullin the attribution match response no longer discards the response. One null query parameter could previously cost an install its attribution data, which surfaced in JavaScript as an emptygetAttributionParams()result.
Changed
- TurboModule migration. The SDK is now a real TurboModule generated by React Native’s codegen, with full backwards compatibility for the legacy architecture. Previously the package shipped no
codegenConfigat all, so codegen never ran and the module was reached through React Native’s legacy interop layer even on the New Architecture. The public JavaScript API is unchanged.- Added a
codegenConfigblock (RNAppstackSdkSpec) and a real TurboModule spec atsrc/NativeAppstackReactNative.ts; the JavaScript entry point now resolves the native module throughTurboModuleRegistry, which works on both architectures. - iOS:
AppstackReactNativeconforms to the generatedNativeAppstackReactNativeSpecprotocol and implementsgetTurboModule:underRCT_NEW_ARCH_ENABLED, falling back toRCTBridgeModuleon the legacy architecture. - Android: the module now extends the generated
NativeAppstackReactNativeSpecon the New Architecture (with aBaseReactPackagethat reportsisTurboModule), and keeps theReactContextBaseJavaModuleimplementation on the legacy architecture. Shared logic moved toAppstackReactNativeModuleImpl.
- Added a
- Breaking:
configure()now rejects a fractionallogLevel.logLevel: 1.5previously passed validation and was silently truncated to1by the native casts (NSIntegeron iOS,Double.toInt()on Android); only the documented levels0,1,2and3are accepted. The error message is nowlogLevel must be one of 0, 1, 2, or 3(waslogLevel must be a number between 0 and 3, which read as though1.5were valid). - Breaking:
sendEventresolvesvoidinstead oftrue. The oldtruewas misleading — it only ever meant “the call reached native”, and was returned even when the native SDK went on to drop the event (automatic-only type, SDK disabled, buffer full, no connectivity). Nothing about delivery is observable from JavaScript, so the return value no longer implies it. Code branching on the result should stop doing so;awaiting still works unchanged. sendEventnow resolves the event type in JavaScript, sending native an explicit(type, name)pair: a standard type with anullname, or theCUSTOMcategory with a name. This fixes a cross-platform divergence in which the same call behaved differently per platform:sendEvent('MY_CUSTOM_EVENT')— an unknown type with no separate name — was sent by iOS as a custom event namedMY_CUSTOM_EVENT, while Android collapsed it toCUSTOMand then rejected it withINVALID_EVENT_NAMEbecause no name had been supplied. Both platforms now receive identical arguments, and the native wrappers’ guessing branches are no longer reachable from JavaScript.- A manual
INSTALLis dropped before reaching native, logging an error and resolving normally rather than throwing. Installs are recorded automatically and sending them by hand inflates install counts, which is why iOS already discarded them natively. The same guard covers the SDK’s internal lifecycle events, which are not part of the publicEventTypeAPI: those exist only in iOS’s enum, so sending one by hand previously became a bogus custom event on Android alone. Resolving rather than rejecting keeps callers who harmlessly send a manualINSTALLtoday — a silent discard since 2.5.0 — from suddenly seeing failures. - Parameter values of
nullorundefinedare stripped before the call reaches native. Android already filtered them out (filterValues { it != null }) while iOS forwardedNSNull, so identical JavaScript produced a different payload per platform. Stripping is shallow, matching Android’s behaviour; an empty result is sent as no parameters at all.0,falseand''are real values and are preserved. parametersis typed against a JSON-safe value type (Record<string, JsonValue | undefined>) instead ofRecord<string, any>. Values outside that set — aDate, a class instance, a function — never survived the bridge intact; this makes that a compile error instead of a runtime surprise.AppstackEventParametersandJsonValueare exported for annotating your own payloads.- Android: the native wrapper now treats an unrecognised event type as a custom event named after that type, matching iOS, instead of rejecting with
INVALID_EVENT_NAME. Defence in depth only — the JavaScript wrapper no longer sends an unrecognised type, so this path is unreachable from JS; it exists so the divergence above cannot return if that invariant is ever broken.
- Breaking: the positional
configure(apiKey, isDebug?, endpointBaseUrl?, logLevel?, customerUserId?)signature, deprecated in 2.6.0, is gone. Onlyconfigure(apiKey, { logLevel, customerUserId })remains.isDebugandendpointBaseUrlare removed outright — neither was ever forwarded to the native SDKs, so no behaviour is lost by dropping them. A 2.x-style call now throws an error naming the replacement instead of silently ignoring the extra arguments, which would otherwise discard thelogLevelandcustomerUserIdthat follow them. - Breaking:
sendEvent(eventType, eventName, parameters)is nowsendEvent(event, parameters). The middleeventNameargument is gone. Pass a standardEventTypefor a standard event, or your own name for a custom one — there is no separate “custom” mode:A three-argument call now rejects with a message naming the replacement, rather than silently binding the event name toparametersand shipping an event whose payload is its own name. (sendEventisasync, so every validation failure rejects the returned promise rather than throwing synchronously.) The two-argumentsendEvent('CUSTOM', 'my_event')form — a custom event with no parameters — is rejected on the same grounds, since its name would otherwise land inparameters.EventTypeitself stays, and remains the recommended form; a plain string is accepted and resolved case-insensitively. - Breaking:
EventType.CUSTOMwas removed. In the two-argument API you pass a custom event’s name directly, soCUSTOMhad no caller-facing meaning and was actively a footgun:sendEvent(EventType.CUSTOM, params)would have resolved as “standard type CUSTOM with no name”, which iOS drops outright and Android sends with a nullevent_name. Removing it turns that into a compile error; the literal string'CUSTOM'is also rejected at runtime, with a message pointing at the custom-name form. - Breaking:
isDebugandendpointBaseUrlare removed from the exportedAppstackConfigtype. - iOS: The native module no longer subclasses
RCTEventEmitter. It never emitted any events (supportedEventsreturned an empty array), and the inheritedaddListener/removeListenersmethods were already unavailable on the New Architecture. Any code callingnew NativeEventEmitter(NativeModules.AppstackReactNative)on the legacy architecture should be removed. - The two deprecation
console.warns that 2.6.0 added for positionalisDebug/endpointBaseUrlare gone with the signature they warned about. - Deleted a stale, unwrapped duplicate of two
AppstackSDK.frameworkslices that was committed directly underios/(plus its orphanedInfo.plist). It was not referenced by the podspec but was published in every npm tarball. The vendored framework atios/AppstackSDK.xcframeworkis unaffected.
- In development builds (
__DEV__),sendEventlogs a warning when the event string is not a recognised standard type, naming the custom event it became. This is the only place a misspelled standard event is catchable: a typo like'PURCAHSE'otherwise becomes a custom event with no error anywhere, silently losing the standard-event semantics that enhanced app campaigns optimise against. It is a heuristic — it also fires on legitimate custom names — so it is advisory, dev-only, and never gates the send.
- iOS: The SDK now builds when the host app forces our pod below iOS 15.0, instead of failing to compile.
AppstackASAAttributionin the vendored framework requires iOS 15.0, and the podspec asks for a 15.0 deployment target — but React Native 0.74 writes its own minimum (13.4) onto every pod target after install, and that setting wins over the podspec. The build then stopped with'AppstackASAAttribution' is only available in iOS 15.0 or newer. The two Apple Search Ads calls are now behind a runtimeif #available(iOS 15.0, *)check. Nothing changes for apps: the Objective-C layer already rejects both methods withUNSUPPORTED_IOS_VERSIONbelow iOS 15.0, and Apple Search Ads attribution cannot work there anyway. iOS 15.0 remains the SDK’s requirement. - iOS: The podspec depended on
React-Codegen, which is an unrelated third-party pod on the public CocoaPods trunk (React Native’s pod isReactCodegen). Dependencies are now installed via React Native’s owninstall_modules_dependencieshelper, which also supplies the codegen and static-framework header search paths that cannot be reproduced by hand. As a side effect, the React-Core header search path fix from 2.1.0 now applies on both architectures instead of only the legacy one. - Android: Removed
cmakeListsPath: nullfromreact-native.config.js. It never disabled codegen autolinking as its comment claimed (nullis falsy, so autolinking always fell through to the default generated path), and it obscured the real reason codegen never ran. - Removed three hand-written files under
android/src/main/jni/that imitated codegen output (AppstackReactNativeSpec.{h,cpp}and aCMakeLists.txtdeclaring a spec target). They contained no TurboModule code, were never built, and are now replaced by genuine generated artifacts.
Added
configure(apiKey, { logLevel, customerUserId })— an options-object form that removes the need to skip the two deprecated positional parameters. Previously, settinglogLevelorcustomerUserIdmeant writingconfigure(key, undefined, undefined, 0, 'user_123'); it is nowconfigure(key, { logLevel: 0, customerUserId: 'user_123' }). This matches the named-parameter shape the Flutter plugin already exposes.AppstackSDK.setCustomerUserId(customerUserId)— sets or clears the customer user ID afterconfigure(), for when the ID is only known once the user logs in. Callingconfigurea second time does not work, because a repeatconfigureis a no-op and ignores itscustomerUserId. It applies to every event sent from that point on; on iOS it also reaches events already buffered but not yet delivered. Clear it on logout so the previous user’s ID stops being attached to later events.null,undefinedand''all clear the stored ID — unlikeconfigure, which rejects an empty string because it never clears. Safe to call at any time; last write wins.- iOS:
getAttributionParams()now always resolves with anappstack_match_statuskey telling you whether the attribution request succeeded:matched,matched_no_params,organic,skipped,failedornot_configured. Onlyfailedis worth re-reading later; the rest are settled answers. Android does not report this key yet, so check for it rather than assuming both platforms return it. - iOS: Attribution match outcomes are now visible in the SDK’s console output at
logLevel0(debug).
- iOS: Updated
AppstackSDK.xcframeworkto4.5.0, which adds the native setter behindsetCustomerUserIdalong with the attribution match status. - Android: Updated the native Appstack Android SDK dependency to
1.7.0, which adds the native setter behindsetCustomerUserId. - iOS: An empty result from
getAttributionParams()no longer means “not attributed” — readappstack_match_statusinstead. - Passing
isDebug: true, or anyendpointBaseUrl, positionally now logs aconsole.warnpointing at the options object; their no-op values (falseandundefined) stay silent. Both arguments remain accepted and continue to be ignored — neither has ever been forwarded to the native SDKs.
- iOS:
deleteUserData()now also clears the stored customer user ID. - iOS: A blank customer user ID is treated as absent instead of being sent as an empty string.
- iOS: A
setCustomerUserIdcall made immediately afterconfigure()is no longer overwritten by the value passed toconfigure().
- The positional signature
configure(apiKey, isDebug?, endpointBaseUrl?, logLevel?, customerUserId?)is unchanged and still type-checks; the two forms are TypeScript overloads and are distinguished at runtime by whether the second argument is an object. All existing validation and error messages are preserved.
Changed
- iOS: Updated
AppstackSDK.xcframeworkto4.4.1. Attribution matching now includes additional network context to improve match diagnostics. - Android: Updated the native Appstack Android SDK dependency to
1.6.0. Attribution matching now includes additional device and network context to improve match accuracy, and install attribution resolves more reliably on first launch. No additional app permissions are required.
- iOS & Android:
sendEventnow ignoresINSTALL, which the SDKs already track automatically. Sending it by hand previously double-counted installs; such calls are now discarded.
Changed
- iOS: Updated
AppstackSDK.xcframeworkto4.4.0. Adds Mac Catalyst support to the prebuilt XCFramework and improves install detection and attribution reliability. - Android: Updated the native Appstack Android SDK dependency to
1.5.0.