Skip to content

Releases: appstack-tech/react-native-appstack-sdk

3.2.0

Choose a tag to compare

@github-actions github-actions released this 04 Sep 10:40
73d6c7c

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.

3.1.0

Choose a tag to compare

@github-actions github-actions released this 03 Sep 11:19
4529ae0

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, revenue and campaign fields — are excluded, and that list is controlled by Appstack's backend. No JavaScript change is required and sendEvent call 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_details and 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 (native 4.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 own PrivacyInfo.xcprivacy are required.

Changed

  • iOS: Updated AppstackSDK.xcframework to 4.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. iOS 4.6.0 adds ASA_ATTRIBUTION to its native event enum and emits it automatically once AdServices token resolution completes, so it joins INSTALL, FIRST_OPEN and FIRST_OPEN_GUARDED in the set the wrapper refuses to forward. It is not part of the public EventType API on either platform.

Fixed

  • iOS: A null nested inside a parameter no longer drops the event. Null values are omitted from the payload and nulls inside arrays are preserved, matching Android. Top-level null and undefined have 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 NaN and infinite numbers, a Date, URL, Set or class instance. Such keys are dropped individually and named in an error log, and the event is sent with its remaining parameters. NaN and Infinity are typed as number, so they satisfy the JsonValue type on parameters.
  • iOS: A single null in 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 empty getAttributionParams() result.

3.0.0

Choose a tag to compare

@github-actions github-actions released this 21 Aug 13:35
c18668d

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 codegenConfig at 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 codegenConfig block (RNAppstackSdkSpec) and a real TurboModule spec at src/NativeAppstackReactNative.ts; the JavaScript entry point now resolves the native module through TurboModuleRegistry, which works on both architectures.
    • iOS: AppstackReactNative conforms to the generated NativeAppstackReactNativeSpec protocol and implements getTurboModule: under RCT_NEW_ARCH_ENABLED, falling back to RCTBridgeModule on the legacy architecture.
    • Android: the module now extends the generated NativeAppstackReactNativeSpec on the New Architecture (with a BaseReactPackage that reports isTurboModule), and keeps the ReactContextBaseJavaModule implementation on the legacy architecture. Shared logic moved to AppstackReactNativeModuleImpl.
  • Breaking: configure() now rejects a fractional logLevel. logLevel: 1.5 previously passed validation and was silently truncated to 1 by the native casts (NSInteger on iOS, Double.toInt() on Android); only the documented levels 0, 1, 2 and 3 are accepted. The error message is now logLevel must be one of 0, 1, 2, or 3 (was logLevel must be a number between 0 and 3, which read as though 1.5 were valid).
  • Breaking: sendEvent resolves void instead of true. The old true was 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.
  • sendEvent now resolves the event type in JavaScript, sending native an explicit (type, name) pair: a standard type with a null name, or the CUSTOM category 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 named MY_CUSTOM_EVENT, while Android collapsed it to CUSTOM and then rejected it with INVALID_EVENT_NAME because 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 INSTALL is 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 public EventType API: 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 manual INSTALL today — a silent discard since 2.5.0 — from suddenly seeing failures.
  • Parameter values of null or undefined are stripped before the call reaches native. Android already filtered them out (filterValues { it != null }) while iOS forwarded NSNull, 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, false and '' are real values and are preserved.
  • parameters is typed against a JSON-safe value type (Record<string, JsonValue | undefined>) instead of Record<string, any>. Values outside that set — a Date, a class instance, a function — never survived the bridge intact; this makes that a compile error instead of a runtime surprise. AppstackEventParameters and JsonValue are 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.

Removed

  • Breaking: the positional configure(apiKey, isDebug?, endpointBaseUrl?, logLevel?, customerUserId?) signature, deprecated in 2.6.0, is gone. Only configure(apiKey, { logLevel, customerUserId }) remains. isDebug and endpointBaseUrl are 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 the logLevel and customerUserId that follow them.
    // 2.x
    await AppstackSDK.configure(apiKey, false, undefined, 0, 'user_123');
    // 3.0
    await AppstackSDK.configure(apiKey, { logLevel: 0, customerUserId: 'user_123' });
  • Breaking: sendEvent(eventType, eventName, parameters) is now sendEvent(event, parameters). The middle eventName argument is gone. Pass a standard EventType for a standard event, or your own name for a custom one — there is no separate "custom" mode:
    // before                                          // after
    sendEvent('PURCHASE', null, { revenue: 4.99 })    sendEvent(EventType.PURCHASE, { revenue: 4.99 })
    sendEvent('CUSTOM', 'user_attributes', params)    sendEvent('user_attributes', params)
    sendEvent('CUSTOM', 'APP_OPENED')                 sendEvent('APP_OPENED')
    A three-argument call now rejects with a message naming the replacement, rather than silently binding the event name to parameters and shipping an event whose payload is its own name. (sendEvent is async, so every validation failure rejects the returned promise rather than throwing synchronously.) The two-argument sendEvent('CUSTOM', 'my_event') form — a custom event with no parameters — is rejected on the same grounds, since its name would otherwise land in parameters. EventType itself stays, and remains the recommended form; a plain string is accepted and resolved case-insensitively.
  • Breaking: EventType.CUSTOM was removed. In the two-argument API you pass a custom event's name directly, so CUSTOM had 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 null event_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: isDebug and endpointBaseUrl are removed from the exported AppstackConfig type.
  • iOS: The native module no longer subclasses RCTEventEmitter. It never emitted any events (supportedEvents returned an empty array), and the inherited addListener / removeListeners methods were already unavailable on the New Architecture. Any code calling new NativeEventEmitter(NativeModules.AppstackReactNative) on the legacy architecture should be removed.
  • The two deprecation console.warns that 2.6.0 added for positional isDebug / endpointBaseUrl are gone with the signature they warned about.
  • Deleted a stale, unwrapped duplicate of two AppstackSDK.framework slices that was committed directly under ios/ (plus its orphaned Info.plist). It was not referenced by the podspec but was published in every npm tarball. The vendored framework at ios/AppstackSDK.xcframework is unaffected.

Added

  • In development builds (__DEV__), sendEvent logs 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.

Fixed

  • iOS: The SDK now builds when the host app forces our pod below iOS 15.0, instead of failing to compile. AppstackASAAttribution in 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 runtime if #available(iOS 15.0, *) check. Nothing changes for apps: the Objective-C layer already rejects both methods with UNSUPPORTED_IOS_VERSION below 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 is ReactCodegen). Dependencies are now installed via React Native's own install_modules_dependencies helper, 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: null from react-native.config.js. It never disabled codegen autolinking as its comment claimed (null is falsy, so autolinking always fell through to the default generated path), and it obscured the real reaso...
Read more

2.6.0

Choose a tag to compare

@github-actions github-actions released this 11 Aug 13:14
9c1c1bc

Added

  • configure(apiKey, { logLevel, customerUserId }) — an options-object form that removes the need to skip the two deprecated positional parameters. Previously, setting logLevel or customerUserId meant writing configure(key, undefined, undefined, 0, 'user_123'); it is now configure(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 after configure(), for when the ID is only known once the user logs in. Calling configure a second time does not work, because a repeat configure is a no-op and ignores its customerUserId. 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, undefined and '' all clear the stored ID — unlike configure, which rejects an empty string because it never clears. Safe to call at any time; last write wins.
  • iOS: getAttributionParams() now always resolves with an appstack_match_status key telling you whether the attribution request succeeded: matched, matched_no_params, organic, skipped, failed or not_configured. Only failed is 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 logLevel 0 (debug).

Changed

  • iOS: Updated AppstackSDK.xcframework to 4.5.0, which adds the native setter behind setCustomerUserId along with the attribution match status.
  • Android: Updated the native Appstack Android SDK dependency to 1.7.0, which adds the native setter behind setCustomerUserId.
  • iOS: An empty result from getAttributionParams() no longer means "not attributed" — read appstack_match_status instead.
  • Passing isDebug: true, or any endpointBaseUrl, positionally now logs a console.warn pointing at the options object; their no-op values (false and undefined) stay silent. Both arguments remain accepted and continue to be ignored — neither has ever been forwarded to the native SDKs.

Fixed

  • 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 setCustomerUserId call made immediately after configure() is no longer overwritten by the value passed to configure().

Compatibility

  • 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.

2.5.0

Choose a tag to compare

@github-actions github-actions released this 06 Aug 12:40
8e60380

Changed

  • iOS: Updated AppstackSDK.xcframework to 4.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.

Fixed

  • iOS & Android: sendEvent now ignores INSTALL, which the SDKs already track automatically. Sending it by hand previously double-counted installs; such calls are now discarded.

2.4.0

Choose a tag to compare

@github-actions github-actions released this 21 Jul 18:04
dfabc7c

Changed

  • iOS: Updated AppstackSDK.xcframework to 4.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.