> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Swift Quickstart

Get started with Dynamic's Swift SDK to add embedded wallets, multi-chain support, and flexible authentication (email, social login, passkeys) to your iOS app in just a few lines.

> [!TIP]
>
> **📱 Complete Example App**: For a fully functional iOS app demonstrating all SDK
> features, check out our [Swift SDK Example App](https://github.com/dynamic-labs-oss/swift-sdk-and-sample-app/tree/main/ExampleApp). It
> includes authentication flows (email/SMS OTP, social login, passkeys), wallet management, EVM/Solana transactions,
> MFA, and passkey examples with SwiftUI.

> [!NOTE]
>
> **Requirements:** iOS 15.0+, Swift 5.9+, Xcode 16.0+, and a Dynamic environment ID from the [Dynamic Console](https://console.dynamic.xyz/dashboard/developer/api).

> [tab] Agent-friendly
>
> > [!TIP]
> >
> > Add the Dynamic docs MCP to your AI editor first — your agent can then query the docs directly.
>
> > [tab] Cursor
> >
> > [Click here to add the MCP to Cursor.](https://cursor.com/en/install-mcp?name=dynamic\&config=eyJ1cmwiOiJodHRwczovL3d3dy5keW5hbWljLnh5ei9kb2NzL21jcCJ9)
>
> > [tab] Claude Code
> >
> > ```bash theme={"system"}
> > claude mcp add --transport http dynamic https://www.dynamic.xyz/docs/mcp
> > ```
>
> > [tab] Codex
> >
> > ```bash theme={"system"}
> > codex mcp add dynamic --url https://www.dynamic.xyz/docs/mcp
> > ```
>
> > [tab] Other
> >
> > If your editor or agent supports remote HTTP MCP, add a server that points at `https://www.dynamic.xyz/docs/mcp`. The exact steps depend on the product; use its MCP documentation for URL-based or HTTP transport setup.
>
> Copy this prompt into your LLM (e.g. Claude, ChatGPT, Cursor) to implement Dynamic in your Swift app.
>
> ````markdown theme={"system"}
> # Swift SDK Quickstart
>
> ## Purpose and Usage
>
> This document provides structured quickstart information for the Dynamic Swift SDK. It is designed to be consumed by LLMs to generate accurate installation commands and code examples based on user requirements.
>
> **How to use this document:**
>
> **BEFORE WRITING ANY CODE**, ask the user which path they want:
>
> > "Would you like a **Quick start** (Xcode + SwiftUI, zero configuration (recommended if you're just getting started)) or a **Custom setup** (choose your installation method and configuration)?"
>
> Do not proceed until the user has answered. Do not assume a path.
>
> - If the user chooses **Quick start**: follow the Quick Start Path section exactly. Do not ask any configuration questions; execute the happy path as written.
> - If the user chooses **Custom setup**: follow the Custom Setup Path section and ask all configuration questions before proceeding.
>
> ---
>
> ## Quick Start Path
>
> > Use this path when the user chooses Quick start. Execute every step in order. Do not skip steps. Do not ask configuration questions.
>
> **Stack:** Xcode + SwiftUI + Swift Package Manager
>
> ### Step 1: Prerequisites
>
> Tell the user:
> - Xcode 16.0+ is required
> - iOS 15.0+ deployment target is required
> - A Dynamic environment ID is required; get one at https://console.dynamic.xyz/dashboard/developer/api
>
> Ask the user for their environment ID before proceeding.
>
> ### Step 2: Install the SDK via Xcode
>
> 1. Open your project in Xcode
> 2. Go to **File → Add Package Dependencies**
> 3. Enter the repository URL: `https://github.com/dynamic-labs-oss/swift-sdk-and-sample-app.git`
> 4. Select version `1.9.0` or later
> 5. Add to your app target
>
> ### Step 3: Configure URL scheme
>
> The SDK requires a URL scheme for authentication callbacks. Add the following to your `Info.plist`:
>
> ```xml
> <key>CFBundleURLTypes</key>
> <array>
>     <dict>
>         <key>CFBundleTypeRole</key>
>         <string>Editor</string>
>         <key>CFBundleURLName</key>
>         <string>com.yourcompany.yourapp</string>
>         <key>CFBundleURLSchemes</key>
>         <array>
>             <string>yourapp</string>
>         </array>
>     </dict>
> </array>
> ```
>
> > Replace `yourapp` with your app's URL scheme (e.g. `myapp`). This must match the `redirectUrl` you set in Step 4.
>
> In the [Dynamic Console](https://console.dynamic.xyz), go to **Security → Whitelist Mobile Deeplink** and add the same deep link URL you will use as `redirectUrl` (e.g. `yourapp://`). Auth callbacks and social login can fail if this URL is not whitelisted.
>
> ### Step 4: Initialize the SDK
>
> In your `@main` App struct, initialize the SDK in `init()` before any views are rendered:
>
> ```swift
> import SwiftUI
> import DynamicSDKSwift
>
> @main
> struct YourApp: App {
>     init() {
>         _ = DynamicSDK.initialize(
>             props: ClientProps(
>                 environmentId: "YOUR_ENVIRONMENT_ID",
>                 appLogoUrl: "https://demo.dynamic.xyz/favicon-32x32.png",
>                 appName: "My App",
>                 redirectUrl: "yourapp://",   // Must match your Info.plist URL scheme
>                 appOrigin: "https://your-domain.com"
>             )
>         )
>     }
>
>     var body: some Scene {
>         WindowGroup {
>             ContentView()
>         }
>     }
> }
> ```
>
> > `environmentId` is the only field `ClientProps` requires, but set all five: `redirectUrl` and `appOrigin` drive the auth callbacks, and `appName` and `appLogoUrl` brand the login UI. `redirectUrl` must match the URL scheme you configured in Step 3.
>
> ### Step 5: Build the content view
>
> Replace the contents of `ContentView.swift`:
>
> ```swift
> import SwiftUI
> import DynamicSDKSwift
> import Combine
>
> @MainActor
> class AuthViewModel: ObservableObject {
>     @Published var isAuthenticated = false
>     @Published var walletAddress: String? = nil
>     private let sdk = try! DynamicSDK.getInstance()
>     private var cancellables = Set<AnyCancellable>()
>
>     init() {
>         isAuthenticated = sdk.auth.authenticatedUser != nil
>
>         sdk.auth.authenticatedUserChanges
>             .receive(on: DispatchQueue.main)
>             .sink { [weak self] user in
>                 self?.isAuthenticated = user != nil
>             }
>             .store(in: &cancellables)
>
>         sdk.wallets.userWalletsChanges
>             .receive(on: DispatchQueue.main)
>             .sink { [weak self] wallets in
>                 self?.walletAddress = wallets.first?.address
>             }
>             .store(in: &cancellables)
>     }
>
>     func signIn() { sdk.ui.showAuth() }
>     func signOut() { Task { try await sdk.auth.logout() } }
> }
>
> struct ContentView: View {
>     @StateObject private var vm = AuthViewModel()
>
>     var body: some View {
>         VStack(spacing: 20) {
>             if vm.isAuthenticated {
>                 Text("Connected!")
>                     .font(.headline)
>                 if let address = vm.walletAddress {
>                     Text(address)
>                         .font(.caption)
>                         .foregroundColor(.secondary)
>                 }
>                 Button("Disconnect") { vm.signOut() }
>                     .foregroundColor(.red)
>             } else {
>                 Button("Connect Wallet") { vm.signIn() }
>                     .buttonStyle(.borderedProminent)
>             }
>         }
>         .padding()
>     }
> }
> ```
>
> ### Step 6: Console configuration checklist
>
> Before running the app, verify these settings in your Dynamic Console at https://console.dynamic.xyz:
>
> 1. **Login method enabled**: go to **Sign-in Methods** and enable at least one (e.g. email OTP)
> 2. **Embedded wallets enabled**: go to **Wallets** and enable embedded wallets
> 3. **Chains enabled**: go to **Chains & Networks** and enable EVM
> 4. **Mobile deep link whitelisted**: go to **Security → Whitelist Mobile Deeplink** and add the exact `redirectUrl` value (e.g. `yourapp://`). It must match `Info.plist` and `ClientProps`.
>
> ### Step 7: Run the app
>
> Build and run in Xcode (`Cmd + R`) or via terminal:
>
> ```bash
> xcodebuild -scheme YourApp -destination 'platform=iOS Simulator,name=iPhone 16' build
> ```
>
> You should see a **Connect Wallet** button. Tapping it opens the Dynamic auth UI.
>
>
> ---
>
> ## Custom Setup Path
>
> > Use this path when the user chooses Custom setup. Ask ALL questions below before generating any code.
>
> **Questions to ask the user:**
>
> 1. Which installation method do you prefer? (Xcode SPM, recommended, or Package.swift)
>
> **Only after receiving answers**, use the sections below to generate the correct setup.
>
> ### Installation Methods
>
> **Xcode (recommended):**
> 1. File → Add Package Dependencies
> 2. URL: `https://github.com/dynamic-labs-oss/swift-sdk-and-sample-app.git`
> 3. Version: `1.9.0` or later
>
> **Package.swift:**
> ```swift
> dependencies: [
>     .package(url: "https://github.com/dynamic-labs-oss/swift-sdk-and-sample-app.git", from: "1.9.0")
> ]
> ```
>
> ### Chains
>
> EVM, Solana, Sui, Bitcoin, TON, Stellar and Tron are all built into the SDK, reachable as `sdk.evm`, `sdk.solana`, `sdk.sui`, `sdk.bitcoin`, `sdk.ton`, `sdk.stellar` and `sdk.tron`. None needs an extra package. Enable the chains you want in **Chains & Networks** in the console.
>
> ### ClientProps
>
> `environmentId` is the only required field. Set `appLogoUrl`, `appName`, `redirectUrl` and `appOrigin` as well: without them the login UI is unbranded and auth callbacks cannot return to the app.
>
> | Property | Description | Example |
> |---|---|---|
> | `environmentId` | Your Dynamic environment ID | `"abc123..."` |
> | `appLogoUrl` | URL to your app logo | `"https://your-app.com/logo.png"` |
> | `appName` | Your app's display name | `"My App"` |
> | `redirectUrl` | URL scheme for auth callbacks (must match `Info.plist`) | `"yourapp://"` |
> | `appOrigin` | Your app's origin domain | `"https://your-app.com"` |
> | `reownProjectId` | Reown project ID for [WalletConnect dApp connectivity](/swift/wallets/global-wallets/walletconnect) (optional) | `"your-reown-project-id"` |
>
> **Console:** In **Security → Whitelist Mobile Deeplink**, add the same value as `redirectUrl` (e.g. `yourapp://`).
>
> ### Documentation
> All docs for this SDK: https://docs.dynamic.xyz (paths starting with `/swift/`)
> Environment ID: https://console.dynamic.xyz/dashboard/developer/api
>
> ---
>
> ## Critical Reference
>
> | Correct | Incorrect | Notes |
> |---|---|---|
> | `DynamicSDK.initialize()` in `@main` App `init()` | Initializing inside a View | Must happen before any views are rendered |
> | `redirectUrl` matches `Info.plist` URL scheme | Mismatched URL scheme | Auth callbacks will silently fail if these don't match |
> | `redirectUrl` whitelisted under **Security → Whitelist Mobile Deeplink** | Deep link not whitelisted | OAuth and return-to-app flows can fail |
> | All 5 `ClientProps` fields set | Omitting `redirectUrl` or `appOrigin` | Only `environmentId` is required, but auth callbacks need `redirectUrl` and `appOrigin` |
> | Combine publishers for auth/wallet state | One-time checks | SDK state is async; always observe publishers |
> | `sdk.wallets.userWalletsChanges` for wallet updates | Reading wallets immediately after auth | Wallets are created asynchronously after authentication |
>
> ---
>
> ## Step-Up Authentication
>
> **Required before accepting the `2026_04_01` API version**: verify your
> minimum API version in [Console > Developers > API & SDK Keys](https://console.dynamic.xyz/dashboard/developer/api).
>
> If using the Dynamic SDK's built-in UI, step-up prompts are handled
> automatically. **If building a headless integration, you must handle
> step-up authentication manually** using
> `sdk.stepUpAuth.isStepUpRequired(scope:)` and
> `sdk.stepUpAuth.promptStepUpAuth(requestedScopes:)`.
>
> See [Step-up authentication](/swift/step-up-auth) for the full implementation guide.
>
> ---
>
> ## Device Registration
>
> **Required before accepting the `2026_04_01` API version**: verify your
> minimum API version in [Console > Developers > API & SDK Keys](https://console.dynamic.xyz/dashboard/developer/api).
>
> If using the Dynamic SDK's built-in UI, device registration is handled
> automatically. **If building a headless integration, you must manually
> check** `sdk.deviceRegistration.isDeviceRegistrationRequired` **and handle
> the completion event yourself.**
>
> Full guide: https://docs.dynamic.xyz/swift/device-registration
>
> ---
>
> ## Troubleshooting
>
> ### 1: "Could not find module 'DynamicSDKSwift'"
> Make sure the package is added to your app target (not just the project). Try Product → Clean Build Folder, then rebuild. Restart Xcode if needed.
>
> ### 2: Auth UI not appearing
> Verify `DynamicSDK.initialize()` is called in `@main` App `init()` before any views load. Do not call `DynamicSDK.getInstance()` before initialization (it throws when the SDK is not initialized).
>
> ### 3: Auth callbacks not working / social login fails
> Verify your URL scheme is configured in `Info.plist` and matches `redirectUrl` in `ClientProps`. Whitelist your deeplink URL in the Dynamic Console under **Security → Whitelist Mobile Deeplink**.
>
> ### 4: Wallets not appearing after login
> Wallets are created asynchronously; use `sdk.wallets.userWalletsChanges` publisher to observe updates. Verify embedded wallets are enabled in the console.
>
> ### 5: Session state not updating
> Use Combine publishers (`authenticatedUserChanges`, `userWalletsChanges`) with `@StateObject` / `@ObservedObject`. Do not read state once; always observe.
>
> If the issue persists, check https://docs.dynamic.xyz/overview/troubleshooting/general.
> ````

> [tab] Manual
>
> [https://documentation-quickstarts.vercel.app/swift?color-scheme=dark](https://documentation-quickstarts.vercel.app/swift?color-scheme=dark)
>
> [https://documentation-quickstarts.vercel.app/swift?color-scheme=light](https://documentation-quickstarts.vercel.app/swift?color-scheme=light)
>
> ## Configure URL Scheme
>
> Add a URL scheme to your `Info.plist` for authentication callbacks:
>
> ```xml Info.plist theme={"system"}
> <key>CFBundleURLTypes</key>
> <array>
>     <dict>
>         <key>CFBundleTypeRole</key>
>         <string>Editor</string>
>         <key>CFBundleURLName</key>
>         <string>com.yourcompany.yourapp</string>
>         <key>CFBundleURLSchemes</key>
>         <array>
>             <string>yourapp</string>
>         </array>
>     </dict>
> </array>
> ```
>
> Use the same URL scheme string as the `redirectUrl` you pass to `ClientProps` (for example `yourapp://`).
>
> ## Whitelist your mobile deep link
>
> In the [Dynamic Console](https://console.dynamic.xyz), open your environment and go to **Security → Whitelist Mobile Deeplink**. Add your app's deep link URL, the same value as `redirectUrl` (e.g. `yourapp://`). Without this, authentication callbacks and social login may not return to your app correctly.
>
> ## Initialize the SDK
>
> The quickstart embed under **Install the SDK** shows package install and SDK initialization. Use the table below for the main `ClientProps` fields.
>
> ### Configuration Options
>
> | Property | Description | Required |
> | - | - | - |
> | `environmentId` | Your Dynamic environment ID from the Dynamic Console | Yes |
> | `appLogoUrl` | URL to your app's logo (shown in auth UI) | No |
> | `appName` | Your app's display name | No |
> | `redirectUrl` | Deep link URL scheme for callbacks | No |
> | `appOrigin` | Your app's origin URL | No |
> | `reownProjectId` | Reown (WalletConnect) project ID for [connecting to dApps](https://docs.dynamic.xyz/docs/swift/wallets/global-wallets/walletconnect.md) | No |
> | `logLevel` | Logging level (`.debug`, `.info`, `.warn`, `.error`) | No |
> | `debug` | Debug options like `ClientDebugProps(webview: true)` | No |
>
> `environmentId` is the only required field. Set `redirectUrl` and `appOrigin` as well so auth callbacks can return to the app, and `appName` and `appLogoUrl` to brand the login UI.
>
> ### Access the SDK
>
> After initialization, access the SDK singleton anywhere in your app:
>
> ```swift theme={"system"}
> let sdk = try! DynamicSDK.getInstance()
> ```
>
> ## Basic Usage
>
> ### Using Built-in Authentication UI
>
> The easiest way to add authentication is using the built-in UI:
>
> ```swift theme={"system"}
> import SwiftUI
> import DynamicSDKSwift
>
> struct ContentView: View {
>     var body: some View {
>         VStack {
>             Button("Sign In") {
>                 try? DynamicSDK.getInstance().ui.showAuth()
>             }
>         }
>     }
> }
> ```
>
> ### Listening for Authentication State
>
> Use Combine publishers to react to authentication changes:
>
> ```swift theme={"system"}
> import SwiftUI
> import DynamicSDKSwift
> import Combine
>
> struct ContentView: View {
>     @StateObject private var vm = ContentViewModel()
>
>     var body: some View {
>         Group {
>             if vm.isAuthenticated {
>                 HomeView()
>             } else {
>                 LoginView()
>             }
>         }
>     }
> }
>
> @MainActor
> class ContentViewModel: ObservableObject {
>     @Published var isAuthenticated = false
>     private let sdk = try! DynamicSDK.getInstance()
>     private var cancellables = Set<AnyCancellable>()
>
>     init() {
>         isAuthenticated = sdk.auth.authenticatedUser != nil
>
>         sdk.auth.authenticatedUserChanges
>             .receive(on: DispatchQueue.main)
>             .sink { [weak self] user in
>                 self?.isAuthenticated = user != nil
>             }
>             .store(in: &cancellables)
>     }
> }
> ```
>
> ### Complete Example with Wallets
>
> ```swift theme={"system"}
> import SwiftUI
> import DynamicSDKSwift
> import Combine
>
> struct HomeView: View {
>     @State private var wallets: [BaseWallet] = []
>     @State private var cancellables = Set<AnyCancellable>()
>
>     private let sdk = try! DynamicSDK.getInstance()
>
>     var body: some View {
>         VStack {
>             Text("Welcome!")
>
>             ForEach(wallets, id: \.address) { wallet in
>                 Text("Wallet: \(wallet.address)")
>             }
>
>             Button("Show Profile") {
>                 sdk.ui.showUserProfile()
>             }
>
>             Button("Logout") {
>                 Task {
>                     try await sdk.auth.logout()
>                 }
>             }
>         }
>         .onAppear {
>             wallets = sdk.wallets.userWallets
>
>             sdk.wallets.userWalletsChanges
>                 .receive(on: DispatchQueue.main)
>                 .sink { newWallets in
>                     wallets = newWallets
>                 }
>                 .store(in: &cancellables)
>         }
>     }
> }
> ```
>
> ## Enable Features in Console
>
> Before you can use authentication and wallet features, enable them in your Dynamic Console:
>
> 1. Go to your [Dynamic Console](https://console.dynamic.xyz/dashboard/overview)
>
> 2. Select your project
>
> 3. Go to **Authentication** and enable the methods you want to use:
>    * Email OTP
>    * SMS OTP
>    * Social providers (Apple, Google, Farcaster)
>    * Passkeys
>
> 4. Go to **Wallets** and enable embedded wallets
>
> 5. Go to **Chains** and enable the networks you want to support (for example EVM and/or Solana; the SDK also ships Sui, Bitcoin, TON, Stellar and Tron modules)
>
> 6. Go to **Security → Whitelist Mobile Deeplink** and add your app's deep link URL (the same value as `redirectUrl`, e.g. `yourapp://`)
>
> > [!TIP]
> >
> > For testing, we recommend starting with Email OTP authentication and an EVM
> > testnet like Base Sepolia. This gives you a complete setup without requiring
> > real phone numbers or mainnet transactions.
>
> ## Troubleshooting
>
> ### Common Issues
>
> * **Could not find module 'DynamicSDKSwift'**
>   * Make sure you've added the package to your target
>   * Try cleaning the build folder (Product → Clean Build Folder)
>   * Restart Xcode
> * **SDK not initialized**
>   * Ensure `DynamicSDK.initialize()` is called in your App's `init()` before any views access the SDK
>   * Don't call `DynamicSDK.getInstance()` before initialization (it throws when the SDK is not initialized)
> * **Authentication callbacks not working**
>   * Verify your URL scheme is configured in `Info.plist`
>   * Ensure the `redirectUrl` matches your URL scheme
>   * Whitelist your deep link URL in the Dynamic Console under **Security → Whitelist Mobile Deeplink**
> * **Wallets not appearing after login**
>   * Wallets are created asynchronously after authentication
>   * Use `sdk.wallets.userWalletsChanges` publisher to listen for wallet updates
>   * Check that embedded wallets are enabled in your console
> * **Session state not updating**
>   * Make sure you're observing the reactive Combine publishers (`authenticatedUserChanges`, `userWalletsChanges`, etc.)
>   * Verify that your views are using `@StateObject` or `@ObservedObject` for view models that observe SDK state
>
> ## Next Steps
>
> > [!WARNING]
> >
> > **Step-up authentication** and **device registration** are required
> > before accepting the `2026_04_01` API version. If you have a headless
> > integration, you must implement both manually.
> > See the [upgrade guide](https://docs.dynamic.xyz/docs/get-started/migrations/api/2026_04_01.md).
>
> * [Step-up authentication](https://docs.dynamic.xyz/docs/swift/step-up-auth.md)
> * [Device registration](https://docs.dynamic.xyz/docs/swift/device-registration.md)
>
> Great! Your SDK is now configured and ready to use. Here's what you can do next:
>
> 1. **[Authentication Guide](https://docs.dynamic.xyz/docs/swift/headless-authentication.md)**: Learn how to implement user authentication with email, SMS, and passkeys
>
> 2. **[Social Authentication](https://docs.dynamic.xyz/docs/swift/social-authentication.md)**: Add social login options like Apple, Google, and Farcaster
>
> 3. **[Wallet Operations](https://docs.dynamic.xyz/docs/swift/wallets/general/token-balances.md)**: Work with balances and sign messages
>
> 4. **[Networks](https://docs.dynamic.xyz/docs/swift/wallets/general/network-management.md)**: Configure blockchain networks
>
> 5. **[Connect to dApps](https://docs.dynamic.xyz/docs/swift/wallets/global-wallets/walletconnect.md)**: Use WalletConnect to connect your embedded wallet to any dApp
>
> > [!NOTE]
> >
> > Building without the Dynamic SDK's built-in UI? See the [Authentication screens](https://docs.dynamic.xyz/docs/swift/headless-authentication.md) for a full list of screens your app needs to handle.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.