Skip to main content

iOS Quickstart

This guide is for our new Kotlin Multiplatform SDK which is in Public beta. If you are instead looking for the stable version, please see stytch-ios and the usage docs here.
This guide walks you through adding Stytch authentication to an iOS app using StytchConsumerSDK or StytchB2BSDK. Both are distributed as a Swift Package from stytchauth/stytch-ios-sdk and require iOS 15.0+.

1. Install the SDK

In Xcode:
  1. Go to File → Add Package Dependencies…
  2. Enter: https://github.com/stytchauth/stytch-ios-sdk
  3. Select version 1.0.0 or later
  4. Add the product that matches your project type:
    • StytchConsumerSDK — Consumer (B2C) apps
    • StytchB2BSDK — B2B apps (organizations/members)
In Package.swift:
dependencies: [
    .package(url: "https://github.com/stytchauth/stytch-ios-sdk", from: "1.0.0"),
],
targets: [
    .target(
        name: "YourTarget",
        dependencies: [
            .product(name: "StytchConsumerSDK", package: "stytch-ios-sdk"),
            // or: .product(name: "StytchB2BSDK", package: "stytch-ios-sdk"),
        ]
    ),
]
Required linker flag: In your target’s Build Settings, add -ObjC to Other Linker Flags.

2. Initialize the Client

Create the client once — at app startup or at the entry point of your auth flow. The client is a singleton; calling createStytchConsumer again returns the same instance.
import StytchConsumerSDK

// Create once and store — e.g., in your App or a top-level singleton
let stytch = createStytchConsumer(
    configuration: .init(
        publicToken: "public-token-live-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    )
)
For B2B apps, import StytchB2BSDK and use createStytchB2B(configuration:) instead. Your public token is in the Stytch Dashboard. Make sure you’ve also enabled the auth methods you want to use under SDK Configuration.

3. Observe Authentication State

The SDK exposes an authenticationStateFlow — an async sequence that emits whenever authentication state changes. Use for await to observe it:
Task {
    for await state in stytch.authenticationStateFlow {
        switch onEnum(of: state) {
        case .authenticated(let s):
            let user = s.user
            let session = s.session
            let sessionToken = s.sessionToken
            // Navigate to your authenticated UI
        case .unauthenticated:
            // Show your login UI
        case .loading:
            // SDK is restoring a persisted session — show a splash screen
        }
    }
}
To read the current state synchronously (e.g., during a navigation check), use stytch.authenticationStateFlow.value.

4. Auth Methods

All SDK methods are async throws. Call them from a Task or an async context and handle errors with do/catch. Errors are thrown as StytchError.

SMS OTP Example

import StytchConsumerSDK

// Step 1: Send OTP
var methodId: String = ""

Task {
    do {
        let params: OTPsSMSLoginOrCreateParameters = .init(phoneNumber: "+15551234567")
        let response = try await stytch.otp.sms.loginOrCreate(request: params)
        methodId = response.methodId
    } catch {
        // Handle error
    }
}

// Step 2: Verify the code
Task {
    do {
        let params: OTPsAuthenticateParameters = .init(
            token: userEnteredCode,
            methodId: methodId,
            sessionDurationMinutes: 30
        )
        try await stytch.otp.authenticate(request: params)
    } catch {
        // Handle error
    }
}

Magic links and password reset emails redirect back to your app via a URL scheme. 1. Register a redirect URL in the Stytch Dashboard. Use a scheme like myapp://auth. 2. Add a URL scheme to your target in Xcode: go to your target’s Info tab, expand URL Types, click +, and enter your scheme (e.g., myapp) in the URL Schemes field. 3. Receive the URL in SwiftUI or UIKit:
// SwiftUI — add to your root view
.onOpenURL { url in
    handleDeeplink(url: url)
}

// UIKit — in your SceneDelegate
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let url = URLContexts.first?.url else { return }
    handleDeeplink(url: url)
}
4. Authenticate the deeplink:
func handleDeeplink(url: URL) {
    Task {
        do {
            let result = try await stytch.authenticate(url: url.absoluteString, sessionDurationMinutes: 30)
            switch onEnum(of: result) {
            case .authenticated:
                // User is now logged in
                break
            case .manualHandlingRequired(let status):
                // Password reset token — prompt for a new password,
                // then call stytch.passwords.resetByEmail(...)
                let resetToken = status.token
            case .unknownDeeplink:
                // Not a Stytch deeplink
                break
            }
        } catch {
            // Handle error
        }
    }
}

6. Session Management

Sessions are automatically persisted across app launches and validated on startup.
// Manually validate the current session (optionally extend it)
let params: SessionsAuthenticateParameters = .init(sessionDurationMinutes: 30)
try await stytch.session.authenticate(request: params)

// Hydrate a client with a session token received out-of-band
try await stytch.hydrate("existing-session-token")

// Sign out
try await stytch.session.revoke()