Quickstart — iOS#

This walkthrough gets a HAAPI-authenticated login working in a fresh iOS app in roughly 15 minutes. It uses the UI Layer (IdsvrHaapiUIKit) because prebuilt screens are the fastest path to a working flow. If your app needs custom screens, start at the layer chooser instead.

Before You Start#

You’ll need:

  • Xcode 15+ and an iOS 14+ deployment target.
  • A Curity Identity Server with a HAAPI-capable OAuth client configured. The clientId, base URL, token endpoint, authorization endpoint, and redirect URI for that client.
  • An iOS device or simulator. If attestation isn’t available on the target device, configure DCR fallback (covered in step 6).

Step 1 — Install the Framework#

The iOS UIKit ships as its own artifact, IdsvrHaapiUIKit. The Driver and SDK are pulled in as transitive dependencies — no separate declarations needed.

dependencies: [
    .package(url: "https://github.com/curityio/ios-idsvr-haapi-sdk-dist")
]
// ... and in your target's dependencies:
.product(name: "IdsvrHaapiUIKit", package: "ios-idsvr-haapi-sdk-dist")

In your Swift files, import IdsvrHaapiUIKit.

Step 2 — Build the Configuration#

Declare the configuration as an AppDelegate property (so it’s built once at instantiation) and build the HaapiUIKitApplication in application(_:didFinishLaunchingWithOptions:):

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var haapiUIKitConfiguration = HaapiUIKitConfigurationBuilder(
        clientId: "my-haapi-client",
        baseUrl: URL(string: "https://idsvr.example.com")!,
        tokenEndpointUrl: URL(string: "https://idsvr.example.com/oauth/v2/oauth-token")!,
        authorizationEndpointUrl: URL(string: "https://idsvr.example.com/oauth/v2/oauth-authorize")!,
        appRedirect: "app://haapi"
    ).build()

    var haapiUIKitApplication: HaapiUIKitApplication!

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        haapiUIKitApplication = HaapiUIKitApplicationBuilder(
            haapiUIKitConfiguration: haapiUIKitConfiguration
        ).build()
        return true
    }
}

This is the same pattern documented in Configuration — your final code should match.

Step 3 — Conform to HaapiFlowResult#

Pick the view controller that will trigger authentication and make it conform to HaapiFlowResult:

class LoginViewController: UIViewController, HaapiFlowResult {
    func didReceiveOAuthTokenModel(_ tokenModel: OAuthTokenModel) {
        // Persist tokens (Keychain) and navigate to authenticated state.
        // REMOVE THIS print BEFORE SHIPPING — it leaks the access token to logs.
        print("Got token: \(tokenModel.accessToken)")
    }

    func didReceiveError(_ error: Error) {
        // Show user-facing error
        print("Flow error: \(error)")
    }
}

Step 4 — Start the Flow#

Trigger the flow on user action — typically a “Sign In” button:

@IBAction func signInTapped(_ sender: UIButton) {
    do {
        try HaapiFlow.start(
            from: self,
            haapiUIKitApplication: UIApplication.shared.haapiUIKitApplication,
            haapiDeepLinkManageable: HaapiDeepLinkManager.shared
        )
    } catch {
        // Configuration error — flow couldn't start
    }
}

extension UIApplication {
    var haapiUIKitApplication: HaapiUIKitApplication {
        guard let appDelegate = delegate as? AppDelegate else {
            fatalError("AppDelegate not configured")
        }
        return appDelegate.haapiUIKitApplication
    }
}

In Info.plist, register your redirect URI scheme (app in the example above) under URL Types. Then in AppDelegate:

func application(
    _ app: UIApplication,
    open url: URL,
    options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
    return HaapiDeepLinkManager.shared.handle(url: url)
}

This lets the framework receive the redirect after an external-browser leg of the flow.

Step 6 — DCR Fallback for Untrusted Devices (Optional)#

When the device doesn’t support hardware-backed key attestation, or when the server rejects the attestation result (older OS versions, certain device states, distribution-channel mismatches), configure DCR fallback in step 2:

let config = HaapiUIKitConfigurationBuilder(
    clientId: "my-haapi-client",
    baseUrl: URL(string: "https://idsvr.example.com")!,
    tokenEndpointUrl: URL(string: "https://idsvr.example.com/oauth/v2/oauth-token")!,
    authorizationEndpointUrl: URL(string: "https://idsvr.example.com/oauth/v2/oauth-authorize")!,
    appRedirect: "app://haapi"
)
.setClientAuthenticationMethod(method: ClientAuthenticationMethodSecret("dev-secret"))
.setDCRConfiguration(configuration: DCRConfiguration(
    templateClientId: "dcr-template-client-haapi-ios",
    clientRegistrationEndpointUrl: URL(string: "https://idsvr.example.com/oauth/oauth-registration")!
))
.build()

The server must have a matching template client configured — see DCR for the server-side prerequisites.

Run It#

Build and run. Tap your sign-in button; the framework presents the login screen, walks the user through the authentication flow, and delivers the access token to didReceiveOAuthTokenModel(_:).

Next: Theming to brand the prebuilt screens · Presentation Options to tune modal vs stack and polling cadence · iOS UIKit for the full topic list

Was this helpful?