Skip to main content

Overview

Server-driven screens let growth teams design and deploy native screens from the AppDNA console — no app release needed. The SDK renders screens from JSON config fetched via Firestore. Screens combine content blocks from any SDK module (onboarding, paywalls, surveys, messages) into a single composable surface. You can build feature announcements, upgrade prompts, referral screens, guided tutorials, and more — all without writing native UI code.

Show a Screen

AppDNA.showScreen("upgrade_prompt") { result in
    print("Screen dismissed: \(result.dismissed)")
    print("Responses: \(result.responses)")
}

Show a Multi-Screen Flow

AppDNA.showFlow("onboarding_v2") { result in
    print("Flow completed: \(result.completed)")
    print("Screens viewed: \(result.screensViewed)")
}

Dismiss

AppDNA.dismissScreen()

Screen Slots (Inline Content)

Place named slots in your SwiftUI views. The console assigns screens to slots.
struct HomeView: View {
    var body: some View {
        VStack {
            AppDNAScreenSlot("home_hero")
            // ... your app content ...
            AppDNAScreenSlot("home_bottom")
        }
    }
}
  • Empty slots render nothing (no visual impact)
  • Content updates on next config refresh
  • Supports audience targeting per slot
Automatically inject screens between app navigations:
// Enable (one-time setup)
AppDNA.enableNavigationInterception()

// Or for specific screens only
AppDNA.enableNavigationInterception(forScreens: ["SettingsVC", "Premium*"])

// Disable
AppDNA.disableNavigationInterception()

Debug Preview

Test screens from raw JSON without publishing to Firestore. previewScreen is only available in #if DEBUG builds.
#if DEBUG
let json = """
{"id":"test","name":"Test","presentation":"modal",
 "layout":{"type":"scroll"},"sections":[...]}
"""
AppDNA.previewScreen(json: json, completion: { result in
    print("Preview dismissed: \(result.dismissed)")
})
#endif
Signature: previewScreen(json: String, completion: ((ScreenResult) -> Void)? = nil)

Screen Delegate

class MyDelegate: AppDNAScreenDelegate {
    func onScreenPresented(screenId: String) { }
    func onScreenDismissed(screenId: String, result: ScreenResult) { }
    func onFlowCompleted(flowId: String, result: FlowResult) { }
    func onScreenAction(screenId: String, action: SectionAction) -> Bool { true }
}

AppDNA.screenDelegate = MyDelegate()

Presentation Modes

Screens support four presentation modes configured in the Console:
ModeDescription
fullscreenFull-screen modal covering the entire screen
modalStandard modal sheet presentation
bottom_sheetDraggable bottom sheet
pushNavigation push (within a navigation controller)

Section Types

Screens are composed of ordered sections. The unified section registry includes content from all SDK modules:
CategorySection Types
Genericcontent_blocks, hero, spacer, divider, cta_footer, sticky_footer
Onboardingonboarding_step, progress_indicator, navigation_controls
Paywallpaywall_header, paywall_plans, paywall_cta, paywall_features, and more
Surveysurvey_question, survey_nps, survey_csat, survey_rating
Messagemessage_banner, message_modal, message_content
Mediaimage_section, video_section, lottie_section, rive_section

Auto-Tracked Events

EventWhen
screen_presentedScreen appears
screen_dismissedScreen disappears
screen_actionUser taps a CTA
flow_startedFlow begins
flow_completedFlow finishes
flow_abandonedFlow dismissed early
slot_renderedSlot displays content
slot_registeredSlot first renders
interception_triggeredNav interception fires

Full Example

import AppDNASDK

class ScreenCoordinator: AppDNAScreenDelegate {
    init() {
        AppDNA.screenDelegate = self
    }

    func showUpgradePrompt() {
        AppDNA.showScreen("upgrade_prompt") { result in
            if result.responses["purchased"] as? Bool == true {
                self.unlockPremium()
            }
        }
    }

    func startFeatureTour() {
        AppDNA.showFlow("feature_tour_v2") { result in
            print("Tour completed: \(result.completed)")
        }
    }

    // MARK: - AppDNAScreenDelegate

    func onScreenPresented(screenId: String) {
        print("Screen shown: \(screenId)")
    }

    func onScreenDismissed(screenId: String, result: ScreenResult) {
        print("Screen dismissed: \(screenId)")
    }

    func onFlowCompleted(flowId: String, result: FlowResult) {
        print("Flow completed: \(flowId), screens viewed: \(result.screensViewed)")
    }

    func onScreenAction(screenId: String, action: SectionAction) -> Bool {
        // Return false to prevent default action handling
        return true
    }
}
Screens are delivered via the same Firestore config bundle as other SDK modules. Ensure your app has network access on first launch to fetch the latest screen configurations.