v1.0.65 (iOS) / v1.0.37 (Android) / v1.0.5 (Flutter) / v1.0.6 (React Native) — Servable surface experiments
Release date: 27 May 2026Added
- 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:)andAppDNA.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 callAppDNA.identify(userId)).
What changed since the previous release
The previous release closed the case where a transaction was already tagged with one user’sappAccountToken / 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.
- 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
identifyin your auth flow. AppDNA.reset()no longer clears the first-identifier anchor. Callingreset()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
| Platform | Version | Notes |
|---|---|---|
| iOS | 1.0.63 | New denyUntaggedOtherUser decision case + first-identifier persistence in UserDefaults |
| Android | 1.0.35 | New DenyUntaggedOtherUser decision case + first-identifier persistence in SharedPreferences |
| Flutter | 1.0.4 | appdna_feature_parity marker bumped to 1.0.63 (no Dart code changes — wraps native iOS + Android) |
| React Native | 1.0.5 | appdna_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 torestorePurchases 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’sobfuscatedAccountId. 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-identifycache 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_idon 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
identifyin your auth flow. The fix is automatic on upgrade. PurchaseOptions.appAccountTokenis still respected and overrides the SDK’s auto-derived token, for the rare case where you want to manage app-user binding yourself.
Versions
| Platform | Version | Notes |
|---|---|---|
| iOS | 1.0.62 | Native fix on both write + read paths |
| Android | 1.0.34 | Native fix on both write + read paths |
| Flutter | 1.0.3 | appdna_feature_parity marker bumped to 1.0.62 (no Dart code changes — wraps native iOS + Android; upgrade to pick up the fix) |
| React Native | 1.0.4 | appdna_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_shadowaccepts 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 tofalse.badge_shape="rectangle"now produces a 2dp-corner badge matching iOS. Previously fell through to pill.pill_selector/minimal_chipsdisplay styles now wrap in horizontal scroll. With 4+ plans the previous equal-weight layout compressed each pill below readable width.segmented_toggledisplay style now uses Material3SingleChoiceSegmentedButtonRowwith the selected plan’s price line below, matching iOSPicker(.segmented).single_herodisplay 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 → 40dpmatching iOS HeaderSection. - Savings text base color
#10B981 → #22C55Eand flips toselected_text_colorwhen 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 → Fitmatching 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 Symbolxmark. - Tooltip CTA shape now reads
button_corner_radius(was reusing the tooltip cardcorner_radius). - Tooltip body
TextAlign.Centerdropped to default leading matching iOS.
16 KB page-size alignment
Bumped Rive9.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
| Platform | Version | Changes |
|---|---|---|
| iOS | 1.0.61 | No change — unchanged from previous release |
| Android | 1.0.33 | All of the above |
| Flutter | 1.0.2 | No change |
| React Native | 1.0.3 | No 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 2026Behavioral change — paywall_trigger nodes skip subscribed users by default
Onboardingpaywall_trigger graph nodes now check hasActiveSubscription() before presenting:
- Each node carries a new
skip_if_subscribed: booleanfield (defaulttrue). - When the user has any active StoreKit (iOS) / Play Billing (Android) entitlement, the SDK skips presentation, emits
onboarding_paywall_skipwithreason=user_already_subscribed, and routes viaon_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.
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:- Fires
onPaywallRestoreCompleted(paywallId, productIds)on your delegate. - Auto-dismisses the paywall surface when
productIdsis non-empty. - Routes the onboarding flow via the trigger’s
on_success_target— same path as a real purchase.
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 viaWebEntitlementManager 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
| Platform | Version | Changes |
|---|---|---|
| iOS | 1.0.61 | All 4 fixes (1A entitlement gate, 1B bridge restore routing, 1C auto-dismiss, 1D identify-time cache refresh) |
| Android | 1.0.32 | All 4 fixes — symmetric mirror of iOS, ships in the same release tag as the SPEC-070-A catch-up |
| Flutter | 1.0.2 | appdna_feature_parity marker bumped to 1.0.61 (no Dart code changes — wraps native iOS + Android) |
| React Native | 1.0.3 | appdna_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 2026License
- 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.
.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.mdin each SDK repo for the per-platform migration timeline, and the migration guide athttps://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 2026Onboarding
- 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 viagenerateStepId— would silently fall through past a paywall step instead of presenting it. The SDK now resolves the nodetypefromgraph_nodesfirst and routes by type, falling back to the legacy prefix check for older flows that still ship without the lightweightgraph_nodesextract. - 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 ingraph_layout.nodes, so flows withpaywall1/analytics1/end1targets 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 2026Paywall
AppDNAPaywallDelegatenow fires for paywalls launched from inside an onboarding flow. Previously, when a paywall opened from an onboardingpresent_paywalloutcome, the SDK swapped in an internal bridge that drove the onboarding chain (success / fail / dismiss → next step) but never forwarded callbacks to your registeredAppDNA.paywall.delegate. Now every method onAppDNAPaywallDelegateis 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.delegatefresh on every callback, so a host that callsAppDNA.paywall.setDelegate(...)after the onboarding flow has been presented still receives forwards.
In-app messages
AppDNAInAppMessageDelegateis now fully wired. All 4 declared methods fire fromMessageManager:onMessageShown(messageId:, trigger:),onMessageAction(messageId:, action:, data:),onMessageDismissed(messageId:), plusshouldShowMessage(messageId:) -> Boolfor host-side display vetoing.shouldShowMessageveto is a behavior change. A host that returnsfalsefromshouldShowMessage(messageId:)now suppresses display AND skips thein_app_message_shownanalytics event. The protocol’s default extension returnstrue, so hosts that don’t implement this method are unaffected.
Surveys
AppDNASurveyDelegateis now fully wired.onSurveyPresented(surveyId:),onSurveyCompleted(surveyId:, responses:), andonSurveyDismissed(surveyId:)all fire fromSurveyManageralongside 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/onRestoreCompletedare 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 directAppDNA.billing.purchase(...)call. For paywall-driven purchases, bothAppDNAPaywallDelegate.onPaywallPurchaseCompletedANDAppDNABillingDelegate.onPurchaseCompletedfire — 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, andonFlowCompletedare now wired alongside the existingonScreenAction. 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 returnstrue, so hosts that have NEVER implemented it are unaffected; only hosts that explicitly returnfalsefrom 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 2026Onboarding
-
14 new strict-typed button actions for authentication and account-lifecycle flows. Configured per-button in the Console as the
actionvalue; the SDK dispatches a structured payload to youronBeforeStepAdvancehandler 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
{action: <string>, ...inputValues}to your delegate, with required-field validation gated the same waynextis. See the new Button Actions section in the iOS onboarding docs for the full payload table and a Swift example. - Authentication entry:
-
request_otp/verify_otpchannel resolution. Both actions include achannelfield ("sms" | "email" | "whatsapp" | "voice") plus therecipientvalue. Resolved from the button’saction_valuefirst, 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 thechannelkey entirely — your handler shouldguard let channel = stepData?["channel"] as? Stringand fail explicitly rather than guess.
Paywall
- 3 new
AppDNAPaywallDelegatemethods 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 emitspurchase_restored/purchase_restore_failedanalytics 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 noAppDNAOnboardingDelegateis set — it logs a warning and stays on the step instead. This generalises the existingsocial_loginprotection to the broader auth catalogue. Hosts that implement the delegate (even with the defaultonBeforeStepAdvancereturning.proceed) are unaffected.
v1.0.51 — Interactive Chat: context round-trip + better webhook error visibility
Release date: April 2026
Interactive Chat (interactive_chat step)
contextfield in chat webhook requests — the SDK now echoes back whatever you returned in a prior response’sdataobject on every subsequent turn ascontext. Lets you integrate threaded AI backends (OpenAI Assistants, hosted LLMs withthreadId, Claude with conversation memory, third-party AI proxies) without replaying the full conversation history on every request. Write once viadata, read every turn fromreq.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_errornow surfaces non-2xx HTTP responses — previously the iOS SDK would decode a 4xx/5xx error body as an emptyChatWebhookResponse(all fields nil) and silently firechat_message_receivedwithmessage_count: 0, rendering nothing. It now checksHTTPURLResponse.statusCode, fireschat_webhook_errorwithhttp_statusand a truncatedresponse_bodyproperty, and shows the configurederror_textin-chat. Android already handled this correctly.
Compatibility
- iOS SDK 1.0.51 (Swift 5.9+, iOS 16+)
- Backwards compatible: new
contextfield 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 2026AppDNA.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.plistis missing - API key validation: Format check at
configure()time (must start withadn_live_oradn_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-existentsandbox-api.appdna.ai) - Main thread fix: iOS
UIApplication.sharedaccess 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.8consistently
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 2026Server-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
AppDNAScreenSlotviews 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 delegate —
AppDNAScreenDelegateprotocol for screen lifecycle callbacks and action interception - 14 new auto-tracked events —
screen_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 2026Onboarding
- 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_viewblocks - Dynamic bindings —
onBeforeStepRenderhook data flows into block properties via{{binding_key}}syntax - Block styling —
block_styledesign 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 handling —
onPromoCodeSubmitdelegate 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 2026SDKs
- 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

