Skip to main content

v1.0.65 (iOS) / v1.0.37 (Android) / v1.0.5 (Flutter) / v1.0.6 (React Native) — Servable surface experiments

Release date: 27 May 2026

Added

  • Servable surface experiments (iOS, Android). You can now A/B test an entire managed surface — a paywall, onboarding flow, in-app message, or survey — with no app code. Create the experiment on the surface in the Console (control references the live entity, treatment carries an alternate configuration); when your app presents that surface the way it always has, the SDK deterministically buckets the user and serves the treatment configuration to the treatment cohort automatically. Control, out-of-audience, and older-SDK users render the live surface unchanged, and the treatment configuration is delivered only inside the experiment, so a non-bucketed user can never receive it. See the Experiments guide.
  • iOS experiment helpers. AppDNA.isInVariant(experimentId:variantId:) and AppDNA.getExperimentConfig(experimentId:key:) are now available on iOS (already present on Android) for reading a code-level assignment or a per-variant config value.

Notes

  • No app code changes are required to adopt servable experiments — update to the SDK version above and configure the experiment in the Console.
  • Flutter and React Native receive the updated configuration models in this release; servable surface rendering follows once their native parity ships, and until then they render the control variant.

v1.0.63 (iOS) / v1.0.35 (Android) / v1.0.4 (Flutter) / v1.0.5 (React Native) — Cross-account entitlement leak follow-up

Release date: 15 May 2026 Follow-up to the v1.0.62 / v1.0.34 hotfix. Strongly recommended for any app where the SDK presents a paywall during onboarding (i.e. before your auth flow has had a chance to call AppDNA.identify(userId)).

What changed since the previous release

The previous release closed the case where a transaction was already tagged with one user’s appAccountToken / obfuscatedAccountId and a different user attempted to restore — that path correctly denies. It also shipped a migration-tolerant policy for untagged historical transactions: granted to whoever was currently identified, so legitimate pre-fix purchases wouldn’t be stranded. That carve-out had a hole. In SDK-driven onboarding flows where the paywall is presented BEFORE the host calls identify(...), the resulting purchase is untagged — every fresh-install purchase, not just legacy ones. Under the previous policy, any user who later signed in on the same device could inherit the previous user’s untagged purchase via Restore. This release scopes the untagged-grant to the device’s first identified user instead of “whoever is identified right now”:
  • The first time AppDNA.identify(userId) runs on a device, the userId is anchored as the device’s first-identifier (persisted across app launches; only cleared by app uninstall or Settings → Apps → Clear data — see host-app guidance below).
  • Untagged transactions are only granted on Restore / silent refresh when the current user matches that anchor — the legitimate self-claim path.
  • Any other user on the same device is denied. The previous owner’s untagged history is not inherited.
The legitimate use cases still work:
  • A user who originally bought via the SDK onboarding paywall, then identifies, signs out, and signs back in as themselves — restore continues to work because they’re the device’s first-identifier.
  • A pre-v1.0.62 customer upgrading the SDK — the first user who identifies after the upgrade claims their own untagged purchase history once. Subsequent users get the strict denial.

Host-app guidance

  • No code changes required if you’re already calling identify in your auth flow.
  • AppDNA.reset() no longer clears the first-identifier anchor. Calling reset() on sign-out is safe — the anchor remains for the lifetime of the app installation. App uninstall, or Settings → Apps → Clear data, is the only path that wipes it. (This is intentional: clearing on sign-out would let the next user identifying on the same device inherit any untagged purchase, which would re-open the leak this release closes.)
  • Call identify(userId) BEFORE allowing a purchase whenever possible. The SDK still completes purchases when no user has identified, but the resulting transaction goes through untagged and now follows the first-identifier policy — be deliberate about which userId you want anchored to those purchases.

Versions

PlatformVersionNotes
iOS1.0.63New denyUntaggedOtherUser decision case + first-identifier persistence in UserDefaults
Android1.0.35New DenyUntaggedOtherUser decision case + first-identifier persistence in SharedPreferences
Flutter1.0.4appdna_feature_parity marker bumped to 1.0.63 (no Dart code changes — wraps native iOS + Android)
React Native1.0.5appdna_feature_parity marker bumped to 1.0.63 (no TypeScript code changes — wraps native iOS + Android)

v1.0.62 (iOS) / v1.0.34 (Android) / v1.0.3 (Flutter) / v1.0.4 (React Native) — Cross-account entitlement security fix

Release date: 15 May 2026 Hotfix across all four SDKs. Strongly recommended for any app where users can sign out and a different user can sign in on the same device — including most subscription apps with per-user accounts.

What changed

The native StoreKit (iOS) and Google Play Billing (Android) APIs that the SDK reads to decide whether a user has an active subscription return entitlements at the device level — tied to the Apple ID / Google account signed in to the device, not the app user signed in to your app. In rare but reproducible scenarios this could cause a different app user, signing in on the same device after the original purchaser had signed out, to be granted the previous user’s subscription on the next call to restorePurchases or after the next identify. This release closes that gap on every read path:
  • On purchase, the SDK now binds every transaction to the currently-identified app user via Apple’s appAccountToken / Google Play’s obfuscatedAccountId. The same deterministic mapping is used on iOS, Android, and (paired with this release) the AppDNA backend, so a user who purchases on one platform and signs in on another is correctly recognized as the owner.
  • On every read of device-level entitlements (restorePurchases, getEntitlements, hasActiveSubscription, the silent post-identify cache refresh), transactions tagged for a different app user are filtered out. Untagged historical purchases (made before this SDK version) are migration-tolerant: granted to the current user once, then claimed server-side so a later user-switch doesn’t silently re-grant them.
  • Server-side, the SDK now sends the authenticated app_user_id on every receipt-verification + restore call. The backend is the authoritative ownership store and applies the same gate end-to-end.

Host-app guidance

  • Call AppDNA.identify(userId: ...) BEFORE allowing a user to purchase. The SDK still completes purchases when no user has identified (preserving first-launch flows), but it logs a warning — those untagged transactions can only be reconciled later via the migration-tolerant path.
  • No code changes are required on your side if you’re already calling identify in your auth flow. The fix is automatic on upgrade.
  • PurchaseOptions.appAccountToken is still respected and overrides the SDK’s auto-derived token, for the rare case where you want to manage app-user binding yourself.

Versions

PlatformVersionNotes
iOS1.0.62Native fix on both write + read paths
Android1.0.34Native fix on both write + read paths
Flutter1.0.3appdna_feature_parity marker bumped to 1.0.62 (no Dart code changes — wraps native iOS + Android; upgrade to pick up the fix)
React Native1.0.4appdna_feature_parity marker bumped to 1.0.62 (no TypeScript code changes — wraps native iOS + Android; upgrade to pick up the fix)

Graphical Asset Generation — one-click on-brand images from your briefs

Release date: 14 May 2026 Console release — no SDK version change.

Generate images straight from a brief

  • One-click image generation across Paid UA briefs, ASO assets, organic social posts, and retention email and in-app message templates — no design-tool round-trip. The generator pulls in your brand kit (palette, typography, logo, imagery guidelines) and the source brief so results are on-brand from the first render.
  • Seven supported asset types — ad images, ASO screenshots, app icons, app-preview thumbnails, social-post graphics, email hero images, and in-app message hero images. Each is generated at the correct platform aspect ratio and resolution automatically.
  • Generate up to four variants per request, turning a multi-variant copy test into a multi-variant image test.

Reference-image editing

  • Attach up to two reference images per request (JPEG, PNG, or WebP, up to 10 MB each) to iterate without rewriting prompts. Choose Edit to use an image as the base for an edit instruction, or Inspiration to use it as style guidance only.

Review, quota, and storage

  • Review before use — every image is shown in a modal with Approve and Regenerate. Approved images are kept; unapproved drafts and reference uploads are removed automatically after 90 days.
  • Monthly image-generation quota per plan, visible on the Billing → Usage page. Enterprise customers with their own provider keys (BYOK) can generate past the included allowance.
  • Admin storage controls — per-plan storage limits, per-tenant overrides, and recurring storage add-ons for Autopilot tenants on the Billing → Packs page.

v1.0.33 (Android only) — Onboarding crash fix + iOS-parity rendering + 16 KB page-size alignment

Release date: 13 May 2026 Android-only hotfix on top of v1.0.32. iOS, Flutter, and React Native are unchanged from the 8 May release.

P0 crash — onboarding step opens

OnboardingActivity crashed with IllegalStateException: Vertically scrollable component was measured with an infinity maximum height constraints the moment a step rendered. Five ContentBlocksScreen variants (image_fullscreen / image_split / image_bottom / image_top / no_image) wrapped the inner zone layout in a redundant outer verticalScroll; the inner layout already provides scrolling, so the nested pair tripped Compose’s infinite-height check. Removed the outer scroll on all five variants.

Onboarding rendering — per-option styling

FormInputSelectBlock (stacked + grid display styles) now honors per-option styling fields from field_options[] (bg_color, selected_bg_color, border_color, selected_border_color, text_color, selected_text_color) and block-level field_config fields including selection_indicator, radio_position, selected_border_width, option_spacing, bg_opacity, and grid_columns. Grid display additionally renders selected_icon / unselected_icon toggle badges. Console-authored styling that previously rendered identically across all options now produces distinct per-option appearance matching iOS. AUTH_ACTIONS_REQUIRING_VALIDATION extended with resend_verification, enable_biometric, logout, and delete_account so the form-validation gate fires before these auth actions, matching iOS.

Paywall rendering — iOS parity

  • card_shadow accepts the String enum (sm / md / lg / none) emitted by the console editor in addition to Boolean. Elevation: sm=2dp, md=4dp, lg=8dp. Previously only Boolean values were honored; String values were silently dropped to false.
  • badge_shape="rectangle" now produces a 2dp-corner badge matching iOS. Previously fell through to pill.
  • pill_selector / minimal_chips display styles now wrap in horizontal scroll. With 4+ plans the previous equal-weight layout compressed each pill below readable width.
  • segmented_toggle display style now uses Material3 SingleChoiceSegmentedButtonRow with the selected plan’s price line below, matching iOS Picker(.segmented).
  • single_hero display style now collapses non-hero plans under an expandable “More options” toggle, matching iOS DisclosureGroup behavior. Previously all plans were always visible.
  • Header section top padding 0 → 40dp matching iOS HeaderSection.
  • Savings text base color #10B981 → #22C55E and flips to selected_text_color when the plan is selected, matching iOS.

Survey rendering — iOS parity

  • Thank-you title font size 16sp → 22sp matching iOS .title2.bold().
  • Free-text input chrome now uses symmetric RoundedCornerShape(8.dp) matching iOS rounded-rect stroke instead of Material3’s default top-only rounding.

In-app message rendering — iOS parity

  • Modal hero image ContentScale.Crop → Fit matching iOS .scaledToFit(). Portrait posters that were center-cropped on Android now render fully.
  • Banner / modal / fullscreen dismiss glyphs Text("✕") → Icon(Icons.Filled.Close) matching iOS SF Symbol xmark.
  • Tooltip CTA shape now reads button_corner_radius (was reusing the tooltip card corner_radius).
  • Tooltip body TextAlign.Center dropped to default leading matching iOS.

16 KB page-size alignment

Bumped Rive 9.11.1 → 9.13.10. Rive 9.13.0 added 16 KB page size support by bundling NDK r27c; the prior catalog claim that 9.11.1 shipped aligned native libs was incorrect. Required for Google Play submissions targeting Android 15+ after Nov 1, 2025.

Affected platforms

PlatformVersionChanges
iOS1.0.61No change — unchanged from previous release
Android1.0.33All of the above
Flutter1.0.2No change
React Native1.0.3No change

v1.0.61 (iOS) / v1.0.32 (Android) / v1.0.2 (Flutter) / v1.0.3 (React Native) — Entitlement-aware paywall triggers + restore routing

Release date: 8 May 2026

Behavioral change — paywall_trigger nodes skip subscribed users by default

Onboarding paywall_trigger graph nodes now check hasActiveSubscription() before presenting:
  • Each node carries a new skip_if_subscribed: boolean field (default true).
  • When the user has any active StoreKit (iOS) / Play Billing (Android) entitlement, the SDK skips presentation, emits onboarding_paywall_skip with reason=user_already_subscribed, and routes via on_success_target (or the downstream edge if no target is set).
  • For upsell paywalls (selling a higher tier to an already-subscribed user), open the trigger in the onboarding flow editor and uncheck Skip if user already has an active subscription.
Legacy flows authored before this release require no migration: nodes without the field default to true at read time. If your app intentionally depends on always-presenting a paywall_trigger (e.g., legal-acceptance screens), explicitly toggle the field off via the console.

Restore now auto-dismisses + routes to success

Tapping Restore Subscription on a paywall presented from an onboarding trigger now:
  1. Fires onPaywallRestoreCompleted(paywallId, productIds) on your delegate.
  2. Auto-dismisses the paywall surface when productIds is non-empty.
  3. Routes the onboarding flow via the trigger’s on_success_target — same path as a real purchase.
Empty productIds means “restore call worked but the user has no entitlements to restore” — paywall stays up so the user can attempt a fresh purchase. A dispatchedDismiss guard inside the SDK ensures dismiss never fires twice; hosts that handle their own dismiss inside the delegate body (or set skipSDKAutoDismiss on iOS) keep working without changes.

AppDNA.identify(userId:) refreshes the entitlement cache

identify(...) now silently calls refreshEntitlementCache() in the background so the next paywall_trigger gate reflects the identified user’s current subscriptions, not the prior anonymous user’s empty entitlement state.

New public API: AppDNA.billing.refreshEntitlementCache()

iOS: await AppDNA.billing.refreshEntitlementCache(). Android: AppDNA.billing.refreshEntitlementCache() (suspend) or refreshEntitlementCacheFuture() (Java-friendly). Call this after host-side auth completes (SSO callbacks, OAuth web flows) to flush stale cache without firing user-visible restore events.

Web-side entitlements limitation

This release’s gate consults StoreKit + Play Billing entitlements only. Stripe / web-side subscriptions tracked via WebEntitlementManager are NOT yet consulted by the gate. If your premium users have only web-side subscriptions, set skip_if_subscribed: false on triggers, or implement a host-side gate via AppDNAOnboardingDelegate.onBeforeStepRender that consults your own backend.

Affected platforms

PlatformVersionChanges
iOS1.0.61All 4 fixes (1A entitlement gate, 1B bridge restore routing, 1C auto-dismiss, 1D identify-time cache refresh)
Android1.0.32All 4 fixes — symmetric mirror of iOS, ships in the same release tag as the SPEC-070-A catch-up
Flutter1.0.2appdna_feature_parity marker bumped to 1.0.61 (no Dart code changes — wraps native iOS + Android)
React Native1.0.3appdna_feature_parity marker bumped to 1.0.61 (no TS code changes — wraps native iOS + Android)

v1.0.60 (iOS) / v1.0.31 (Android) / v1.0.1 (Flutter) / v1.0.2 (React Native) — License change + small API additions

Release date: 5 May 2026

License

  • All four SDKs (iOS, Android, Flutter, React Native) move from MIT to the AppDNA SDK Proprietary License. The new license is publicly visible for marketing, evaluation, and reference purposes only — actual use of the SDK requires a Commercial Agreement with AppDNA AI, Inc.
  • v1.0.31 (Android), v1.0.1 (Flutter), v1.0.2 (React Native) are license-only. v1.0.60 (iOS) bundles the license switch with two small API additions (below). No breaking changes on any platform.
Note on React Native version: v1.0.1 was previously published to npm under the MIT license; v1.0.2 is the first React Native release on the proprietary license. Cutover docs anchor on v1.0.2 (not v1.0.1) for that reason.
  • Existing customers operating under a Commercial Agreement (Terms of Service, Statement of Work, or Master Services Agreement) continue to use the SDK under those existing terms — nothing changes operationally.

iOS — New: email_login action

The email provider button in the social_login content block now emits action: "email_login" instead of (only) action: "social_login". The social_login action is still emitted in parallel one final release for backward compatibility — it will be removed in v1.1.0. If your AppDNAOnboardingDelegate switches on action, add a case "email_login": branch that handles your email-auth flow (typically: read email and password from responses and call your auth backend). Existing case "social_login": handlers that special-case value == "email" keep working until v1.1.0; migrate to the new action when convenient. OAuth providers (Apple, Google) continue to emit action: "social_login" as before — no change.

iOS — New: StepAdvanceResult.stay(message:)

Added to the StepAdvanceResult enum returned by onBeforeStepAdvance. Use it when your hook handled the user’s action (e.g., sent a password-reset email, displayed a popup yourself) and you want the user to remain on the same step without advancing and without showing an error.
// Truly silent — your code already showed UI
return .stay()

// Non-empty message renders as a green success banner
return .stay(message: "Reset email sent to \(email)")
This is distinct from .block(message:), which renders in error styling (red). Use .block for failures, .stay for “I handled this; keep the user here”. Server-side hook responses can also return action: "stay" with an optional message field.

Migration deadline — 15 May 2026

  • MIT-licensed versions are deprecated. AppDNA’s servers will stop accepting connections from MIT versions on 15 May 2026. Update your dependency to the new proprietary version before that date to avoid service disruption.
  • App Store / Google Play review can take 24-48h or longer — submit your release for review by 10 May 2026 to ensure approval and rollout in time.
  • See DEPRECATION_NOTICE.md in each SDK repo for the per-platform migration timeline, and the migration guide at https://docs.appdna.ai/migrations/v1-0-to-v1-1.

Compatibility

  • iOS SDK 1.0.60 (Swift 5.9+, iOS 16+) — drop-in replacement for 1.0.59.
  • Android SDK 1.0.31 (Kotlin 1.9+, API 24+) — drop-in replacement for 1.0.30.
  • Flutter SDK 1.0.1 — drop-in replacement for 1.0.0.
  • React Native SDK 1.0.1 — drop-in replacement for 1.0.0.

v1.0.59 — Onboarding routing accepts both modern and legacy graph-node IDs

Release date: May 2026

Onboarding

  • Paywall, end, and analytics graph nodes now route correctly under both the modern short ID format (paywall1, analytics2, end1) and the legacy timestamp format (paywall_trigger_<ts>, end_<ts>, analytics_event_<ts>). The renderer previously identified graph-node targets by ID prefix only, so flows authored in the current Console editor — which generates short IDs via generateStepId — would silently fall through past a paywall step instead of presenting it. The SDK now resolves the node type from graph_nodes first and routes by type, falling back to the legacy prefix check for older flows that still ship without the lightweight graph_nodes extract.
  • This is a publish-side fix as well: the platform validator was rejecting modern IDs at publish time with INVALID_NEXT_STEP_TARGET. That check now also looks up the target’s node type in graph_layout.nodes, so flows with paywall1 / analytics1 / end1 targets publish successfully.

Compatibility

  • iOS SDK 1.0.59 (Swift 5.9+, iOS 16+)
  • Backwards compatible. Legacy prefix-form IDs continue to route exactly as before; flows previously published with paywall_trigger_<ts> keep working without re-publishing.
  • Android coverage for the same routing gap will ship in the next Android release.

v1.0.58 — Full delegate coverage across paywall, survey, in-app message, billing, push, and screens

Release date: April 2026

Paywall

  • AppDNAPaywallDelegate now fires for paywalls launched from inside an onboarding flow. Previously, when a paywall opened from an onboarding present_paywall outcome, the SDK swapped in an internal bridge that drove the onboarding chain (success / fail / dismiss → next step) but never forwarded callbacks to your registered AppDNA.paywall.delegate. Now every method on AppDNAPaywallDelegate is forwarded to your delegate before the onboarding routing logic runs — host apps get full visibility into purchase, restore, action, promo-code, and post-purchase callbacks regardless of how the paywall was launched.
  • Late delegate registration is supported. The bridge reads AppDNA.paywall.delegate fresh on every callback, so a host that calls AppDNA.paywall.setDelegate(...) after the onboarding flow has been presented still receives forwards.

In-app messages

  • AppDNAInAppMessageDelegate is now fully wired. All 4 declared methods fire from MessageManager: onMessageShown(messageId:, trigger:), onMessageAction(messageId:, action:, data:), onMessageDismissed(messageId:), plus shouldShowMessage(messageId:) -> Bool for host-side display vetoing.
  • shouldShowMessage veto is a behavior change. A host that returns false from shouldShowMessage(messageId:) now suppresses display AND skips the in_app_message_shown analytics event. The protocol’s default extension returns true, so hosts that don’t implement this method are unaffected.

Surveys

  • AppDNASurveyDelegate is now fully wired. onSurveyPresented(surveyId:), onSurveyCompleted(surveyId:, responses:), and onSurveyDismissed(surveyId:) all fire from SurveyManager alongside the existing analytics events. Responses arrive as the public [SurveyResponse] shape so you can attach them to your own analytics or CRM.

Billing

  • AppDNABillingDelegate.onPurchaseCompleted / onPurchaseFailed / onRestoreCompleted are now wired. The 3 lifecycle methods fire from the billing bridges (StoreKit2Bridge, RevenueCatBridge, AdaptyBridge) regardless of whether the purchase came from a paywall or a direct AppDNA.billing.purchase(...) call. For paywall-driven purchases, both AppDNAPaywallDelegate.onPaywallPurchaseCompleted AND AppDNABillingDelegate.onPurchaseCompleted fire — each represents a different layer (paywall-lifecycle vs platform-billing). Pick one as your source of truth based on the granularity you need.

Push

  • AppDNAPushDelegate.onPushTokenRegistered(token:) is now wired. Fires whenever the SDK registers a new APNs token (or detects a change). Token registration with our backend continues to happen regardless of whether the host has a delegate.

Screens (server-driven UI)

  • AppDNAScreenDelegate.onScreenPresented, onScreenDismissed, and onFlowCompleted are now wired alongside the existing onScreenAction. Hosts get the full lifecycle for screens and flows composed via the SDUI engine.

Compatibility

  • iOS SDK 1.0.58 (Swift 5.9+, iOS 16+)
  • Backwards compatible. Every protocol involved already had empty default extensions, so hosts that don’t implement the newly-wired methods see no behavior change. The one exception is shouldShowMessage — its default returns true, so hosts that have NEVER implemented it are unaffected; only hosts that explicitly return false from a custom implementation will now see messages suppressed (which is the contract the method always promised).
  • Android coverage for the same delegate gaps will ship in a follow-up release. iOS-only this version.

v1.0.57 — Strict-typed auth actions + restore purchase callbacks

Release date: April 2026

Onboarding

  • 14 new strict-typed button actions for authentication and account-lifecycle flows. Configured per-button in the Console as the action value; the SDK dispatches a structured payload to your onBeforeStepAdvance handler so you can route to the right backend call and stay on the step while async auth runs.
    • Authentication entry: login, register, reset_password, magic_link, request_otp, verify_otp, verify_email, resend_verification, enable_biometric
    • Account lifecycle: logout, change_password, set_new_password, delete_account, update_profile
    Each action emits {action: <string>, ...inputValues} to your delegate, with required-field validation gated the same way next is. See the new Button Actions section in the iOS onboarding docs for the full payload table and a Swift example.
  • request_otp / verify_otp channel resolution. Both actions include a channel field ("sms" | "email" | "whatsapp" | "voice") plus the recipient value. Resolved from the button’s action_value first, falling back to auto-detect when a step has exactly one phone-typed or email-typed input. Steps with both an email AND a phone input (or neither) omit the channel key entirely — your handler should guard let channel = stepData?["channel"] as? String and fail explicitly rather than guess.

Paywall

  • 3 new AppDNAPaywallDelegate methods for the Restore Purchases lifecycle: onPaywallRestoreStarted(paywallId:), onPaywallRestoreCompleted(paywallId:, productIds:), onPaywallRestoreFailed(paywallId:, error:). Previously, tapping a paywall’s Restore button ran successfully against StoreKit 2 but the host app received no callbacks — only an internal log line. Now the SDK fires the full lifecycle and emits purchase_restored / purchase_restore_failed analytics events.

Compatibility

  • iOS SDK 1.0.57 (Swift 5.9+, iOS 16+)
  • Backwards compatible: the 5 existing button actions (next, skip, link, social_login, permission) are unchanged. The 3 new paywall delegate methods ship with default empty implementations so existing host apps don’t need code changes after upgrade.
  • No-delegate guard. When any auth-style action fires (the 14 new + the existing social_login), the SDK now refuses to silently advance past the credential-collection step if no AppDNAOnboardingDelegate is set — it logs a warning and stays on the step instead. This generalises the existing social_login protection to the broader auth catalogue. Hosts that implement the delegate (even with the default onBeforeStepAdvance returning .proceed) are unaffected.

v1.0.51 — Interactive Chat: context round-trip + better webhook error visibility

Release date: April 2026

Interactive Chat (interactive_chat step)

  • context field in chat webhook requests — the SDK now echoes back whatever you returned in a prior response’s data object on every subsequent turn as context. Lets you integrate threaded AI backends (OpenAI Assistants, hosted LLMs with threadId, Claude with conversation memory, third-party AI proxies) without replaying the full conversation history on every request. Write once via data, read every turn from req.body.context. Last-write-wins across turns per-key. See the new Threaded backends section in the iOS onboarding docs for the full pattern.
  • chat_webhook_error now surfaces non-2xx HTTP responses — previously the iOS SDK would decode a 4xx/5xx error body as an empty ChatWebhookResponse (all fields nil) and silently fire chat_message_received with message_count: 0, rendering nothing. It now checks HTTPURLResponse.statusCode, fires chat_webhook_error with http_status and a truncated response_body property, and shows the configured error_text in-chat. Android already handled this correctly.

Compatibility

  • iOS SDK 1.0.51 (Swift 5.9+, iOS 16+)
  • Backwards compatible: new context field is additive. Older webhooks that don’t read it keep working. Older SDKs that don’t send it keep working against new webhooks.

v1.0.12 — Diagnostics & Error Handling

Release date: March 2026
  • AppDNA.diagnose(): New public method that prints a full SDK health report — API key validation, bootstrap status, Firebase initialization path, identity, event queue, and active modules
  • Smart event retry: SDK no longer retries on 401 (invalid key) or 400 (bad payload) — pauses immediately instead of wasting 15 retry cycles
  • Event upload logging: HTTP status code and response body now logged on every failure (was silent before)
  • Firebase safety: Removed dangerous fallback to host app’s Firebase project. Clear error messages when GoogleService-Info-AppDNA.plist is missing
  • API key validation: Format check at configure() time (must start with adn_live_ or adn_test_)
  • Bootstrap timeout: Increased from 5s to 15s (was shorter than retry cycle on cellular networks)
  • Crash prevention: Event flush pauses after 5 consecutive failures + 1000 event memory cap
  • Android timeout fixes: Rich push image download 5s→10s/15s, geocoding 5s→10s, hardcoded URL→api.appdna.ai

Compatibility

  • iOS SDK 1.0.12 (Swift 5.9+, iOS 16+)
  • Android SDK 1.0.12 (Kotlin, API 24+)

v1.0.9 — Critical Fixes

Release date: March 2026
  • Firebase crash fix: iOS SDK no longer crashes when host app already uses Firebase — uses named secondary app (“appdna”) with 4-tier fallback
  • API URL fix: Both sandbox and production environments now use api.appdna.ai (removed non-existent sandbox-api.appdna.ai)
  • Main thread fix: iOS UIApplication.shared access moved to main thread, preventing background-thread crashes
  • Android null safety: Fixed 3 force-unwrap crashes in bootstrap parsing and Firebase config
  • Android connection leak: All HTTP responses now use .use {} block to prevent connection pool exhaustion
  • Thread safety: Added NSLock for iOS initialization, synchronized blocks for Android module access
  • 31 additional fixes from comprehensive SDK audit (force unwraps, race conditions, error handling)

Compatibility

  • iOS SDK 1.0.9 (Swift 5.9+, iOS 16+)
  • Android SDK 1.0.9 (Kotlin, API 24+)

v1.0.8 — Stability & Compatibility

Release date: March 2026
  • Firebase compatibility: iOS SDK now supports Firebase 11.x and 12.x (>= 11.0, < 13.0)
  • CocoaPods fix: Added MIT LICENSE file (required for pod trunk push)
  • Version alignment: iOS and Android SDKs both report 1.0.8 consistently

Compatibility

  • iOS SDK 1.0.8 (Swift 5.9+, iOS 16+)
  • Android SDK 1.0.8 (Kotlin, API 24+)

v1.0.50 — Server-Driven UI Engine (SPEC-089c)

Release date: March 2026

Server-Driven Screens (New Module)

  • Unified screen renderer — any screen designed in the console is rendered natively, combining content blocks from onboarding, paywalls, surveys, and messages into a single composable surface
  • Multi-screen flows — build guided tours, upgrade funnels, win-back sequences, and onboarding alternatives with server-defined navigation graphs and branching
  • Screen slots — embed named AppDNAScreenSlot views inline in your app; the console assigns and updates content with audience targeting
  • Navigation interception — automatically inject screens between app navigations without code changes
  • Debug preview — test screen configs from raw JSON without publishing to Firestore
  • Screen delegateAppDNAScreenDelegate protocol for screen lifecycle callbacks and action interception
  • 14 new auto-tracked eventsscreen_presented, screen_dismissed, screen_action, flow_started, flow_completed, flow_abandoned, slot_rendered, slot_registered, interception_triggered, and more

Paywall Enhancements

  • 12 plan display styles — horizontal cards, vertical stack, toggle switch, segmented control, radio list, carousel, comparison grid, pill selector, tier blocks, minimal text, featured highlight, and accordion
  • Card & badge customization — per-plan badge text/color, card border/shadow, save percentage, trial label, card background (solid/gradient/image), and corner radius

Onboarding Enhancements

  • Row direction and distribution — configurable horizontal/vertical direction with 7 distribution modes (equal, fill, start, center, end, space_between, space_around)
  • Button gradients — gradient backgrounds with configurable direction (horizontal, vertical, diagonal)
  • Select display styles — dropdown, stacked, and grid layouts for question option lists
  • Progress bar custom colors — custom fill color, track color, gradient fill, corner radius, and height

Platforms

  • iOS SDK 1.0.50 (Swift 5.9+, iOS 16+)
  • Android SDK 1.0.50 (Kotlin, API 24+)

v1.0.6 — SDK Rendering Parity (SPEC-089d)

Release date: March 2026

Onboarding

  • 18 new content block types — page indicator, wheel picker, pulsing avatar, social login, timeline, animated loading, countdown timer, rating, rich text, progress bar, circular gauge, date wheel picker, stack, row, custom view, star background, and pricing card
  • Custom view registration — host apps can register native SwiftUI/Composable views for custom_view blocks
  • Dynamic bindingsonBeforeStepRender hook data flows into block properties via {{binding_key}} syntax
  • Block stylingblock_style design tokens for padding, margin, background, corner radius, border, shadow, and opacity
  • Visibility conditions — per-block show/hide rules based on responses, bindings, device attributes
  • Entrance animations — fade, slide, scale, flip, and bounce animations per block with configurable duration and easing
  • 8 new form field types — password, rating, range slider, image picker, color, URL, chips, and signature

Paywalls

  • 12 new section types — countdown, legal, divider, sticky footer, card, carousel, timeline, icon grid, comparison table, promo input, toggle, and reviews carousel
  • Promo code handlingonPromoCodeSubmit delegate for server-side coupon validation with discount display
  • Toggle state in purchase metadata — toggle section states (e.g., annual billing, add-ons) included in transaction metadata

Bug Fixes

  • Fixed 3 remaining audit failures for SPEC-089d rendering compliance
  • Added flip animation type to Android entrance animations

Platforms

  • iOS SDK 1.0.6 (Swift 5.9+, iOS 16+)
  • Android SDK 1.0.6 (Kotlin, API 24+)

v1.0.0 — General Availability

Release date: February 2026

SDKs

  • iOS SDK 1.0.0 (Swift 5.9+, iOS 16+, SPM & CocoaPods)
  • Android SDK 1.0.0 (Kotlin, API 24+, Maven Central)
  • Flutter SDK 1.0.0 (Dart 3.0+, pub.dev)
  • React Native SDK 1.0.0 (TypeScript, npm)

Core Features

  • Offline-first architecture — Three-tier config priority (Remote > Cached > Bundled)
  • Config Bundles — Embeddable versioned JSON for CI/CD pipelines
  • Identity management — Anonymous ID generation, user identification with traits, cross-session persistence
  • Event tracking — Custom events with structured properties, automatic batching and retry

Modules

  • Push Notifications — Token registration, rich notifications, deep link handling
  • Billing — StoreKit 2 (iOS), Google Play Billing 7.0 (Android), entitlement management, receipt verification
  • Onboarding — Server-driven onboarding flows with step tracking
  • Paywalls — Server-driven paywall presentation with A/B testing support
  • Remote Config — Key-value configuration with real-time updates
  • Feature Flags — Boolean feature gates with targeting rules
  • A/B Experiments — Deterministic MurmurHash3 assignment, cross-platform consistent bucketing
  • Surveys — In-app surveys with response collection
  • In-App Messages — Triggered messaging with templates
  • Deep Links — Universal/App link handling with deferred deep link support

API & Integrations

  • RESTful API with two authentication modes (SDK keys & Dashboard keys)
  • 16 webhook event types with HMAC-SHA256 signing
  • Exponential backoff retry (5 attempts)

Dashboard

  • 10 growth modules (Onboarding, Monetization, Retention, ASO, ICP/USP, Paid UA, Organic Social, Feedback Loop, Web2App, Experiments)
  • AI-powered strategy assistant per module
  • Real-time Event Debugger
  • Organization and app management with role-based access