Preview Tools (UI Layer)#

The Preview Tools let you iterate on Theme.plist (iOS) or theme styles in themes.xml / styles.xml (Android) and see the result on themed HAAPI screens without rebuilding the running app. Both platforms ship debug-only preview utilities that integrate with their native IDE tooling — Xcode’s Preview canvas on iOS, Android Studio’s Compose @Preview on Android (complemented by a Previewer Host Activity for production-fidelity validation; see Previewer Host Activity ). Use them as the day-to-day surface for theming work; production validation still happens against the running flow.

For the theming surfaces themselves, see Theming .

Experimental API on both platforms. iOS — HaapiUIPreviewer, HaapiUIPreviewerRepresentable, HaapiUIPreviewerStyleProvider, HaapiUIPreviewerWebAuthnVariant are experimental; minor releases may change them without bumping the major version. Android — all public previewer types are annotated with @ExperimentalHaapiApi and require @OptIn(ExperimentalHaapiApi::class) at use sites. Feedback to Curity is welcomed.

What’s Available#

Both platforms cover the same set of HAAPI screen types and most component galleries. Android additionally ships a Previewer Host Activity that runs the full production Fragment pipeline for theme-fidelity validation.

iOS UIKitAndroid UIWidget
IDE integrationXcode #Preview macro / PreviewProviderAndroid Studio Compose @Preview + Previewer Host Activity
Build configurationDEBUG only (#if DEBUG)src/debug/ source set only
Screen previews7 (Form, Selector, Polling, BankID, Problem, Generic, WebAuthn)7 (same set)
Component galleries9 (button, message, input, header, checkbox, loading, link, notification banner, expandable)10 (button, message, input, checkbox, header, link, loading, spinner-text, expandable, snackbar) ¹
WebAuthn variantsRegistration, Authentication, Additional Registration, Platform-OnlySame four variants
Production-fidelity validationn/a — single rendering path Previewer Host Activity runs real Fragments with real FragmentManager lifecycle

¹ The platforms name their transient-feedback gallery differently — iOS calls it notificationBannerGallery, Android calls it SnackbarGallery — but they cover the same UI concept (success and error toast-style messages). Android additionally exposes a SpinnerTextViewGallery for the dropdown selector component, which is what brings the count from 9 to 10.

Setup and Usage#

Requirements#

  • iOS 14.0+ runtime target
  • Xcode 15+ for the #Preview macro; PreviewProvider works on earlier Xcode versions
  • DEBUG builds only — all preview types are wrapped in #if DEBUG, so they don’t ship in your release binary

Minimal Preview#

Create a Swift file in your app target (a Previews/ group is a common convention) and add:

import SwiftUI
import IdsvrHaapiUIKit

#Preview("Login Form") {
    try! HaapiUIPreviewer.formViewController()
}

Open the Xcode canvas (Editor → Canvas or Cmd+Option+Return) and click Resume if needed. A login form renders with the default HAAPI theme.

Using Your Custom Theme#

Pass the plist name (without extension) to render with your app’s theme:

#Preview("Login Form — My Brand") {
    try! HaapiUIPreviewer.formViewController(theme: "MyCustomTheme")
}

In the preview context, Bundle.main points to Xcode’s preview host rather than your app. If the theme doesn’t appear, pass the bundle explicitly:

#Preview("Login Form — My Brand") {
    try! HaapiUIPreviewer.formViewController(
        theme: "MyCustomTheme",
        bundle: Bundle(for: AppDelegate.self)
    )
}

Screen-Level Previews#

Seven factory methods cover every prebuilt view controller. Each maps to the matching production class documented in UI Extensibility ’s Default Model → View Mapping table. All accept optional custom HAAPI JSON, a theme name, a bundle, and an embeddedInFlow flag (renders inside a flow container matching production HaapiFlowViewController layout):

  • formViewController — renders FormViewController
  • selectorViewController — renders SelectorViewController
  • problemViewController — renders ProblemViewController
  • pollingViewController — renders PollingViewController
  • bankIdViewController — renders BankIdViewController
  • genericViewController — renders GenericViewController
  • webAuthnViewController — renders WebAuthnViewController; also accepts variant: HaapiUIPreviewerWebAuthnVariant to choose between registration, authentication, additional registration, and platform-only (timeout/retry) layouts

Component Galleries#

Nine gallery methods render every style variant of a single component side-by-side:

  • actionableButtonGallery (Primary, Secondary, Text, LinkView variants)
  • messageViewGallery (Error, Warning, Info, Heading, Content, Username, RecipientOfCommunication, UserCode)
  • inputTextFieldGallery (Curity, Filled, Outlined — each with normal and error states)
  • headerViewGallery (default header view)
  • checkboxViewGallery (unchecked, checked)
  • loadingIndicatorGallery (indeterminate progress + per-button variants)
  • linkViewGallery (link view)
  • notificationBannerGallery (default + success variants — toast-style transient feedback)
  • expandableViewGallery (collapsed, expanded)

Custom HAAPI JSON#

Each factory method accepts a JSON fixture you can supply to render a non-default state — login form with prefilled errors, polling screen mid-countdown, etc.:

#Preview("Login Form — With Error") {
    try! HaapiUIPreviewer.formViewController(
        haapiJson: MyFixtures.loginWithError,
        theme: "MyCustomTheme"
    )
}

Preview factory methods throw on misconfiguration — bad JSON, missing plist key, malformed style value. Use try! for fast feedback during development; the thrown error message points at the offending key.

Workflow Tips#

  • Swift code changes in #Preview blocks refresh automatically once the canvas resumes.
  • Theme.plist changes require the canvas to be rebuilt (the Resume button) — Xcode does not re-parse plists on every keystroke.
  • Keep the Xcode console open the first time you wire a new theme. Plist parse errors and missing style keys log there with a descriptive message.
  • Group preview files under a Previews/ folder per the convention; this also makes them easy to exclude from production-test code-coverage reports.
  • SwiftUI-only apps without an AppDelegate class can pass Bundle(identifier: "com.example.app") to the bundle: parameter instead of Bundle(for: AppDelegate.self).

Both mechanisms render static UI from JSON fixtures — no network requests, no real authentication, no navigation between screens. Button / link taps either no-op (iOS Xcode preview, Android Compose @Preview) or enter a stuck loading state (Android Previewer Host Activity). Use these for visual validation; for flow-behavior verification, run the real app against a Curity Identity Server.

Was this helpful?