📱 Complete Example App: For a fully functional iOS app demonstrating all SDK
features, check out our Swift SDK Example App. It
includes authentication flows (email/SMS OTP, social login, passkeys), wallet management, EVM/Solana transactions,
MFA, and passkey examples with SwiftUI.
Requirements: iOS 15.5+, Swift 5.9+, Xcode 14.0+, and a Dynamic environment ID from the Dynamic dashboard.
- Agent-friendly
- Visual
Add the Dynamic docs MCP to your AI editor first — your agent can then query the docs directly. Instructions are on the Welcome page.
# 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 14.0+ is required
- iOS 15.5+ deployment target is required
- A Dynamic environment ID is required — get one at https://app.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/swift-sdk-and-sample-app.git`
4. Select version `1.0.2` 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 dashboard](https://app.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()
}
}
}
```
> All five `ClientProps` fields are required. `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 = DynamicSDK.instance()
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 — 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.
### Step 7 — Dashboard configuration checklist
Before testing auth, verify these settings in your Dynamic dashboard at https://app.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`.
---
## 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)
2. Which chains do you want to support? (EVM, SVM/Solana — one or both)
**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/swift-sdk-and-sample-app.git`
3. Version: `1.0.2` or later
**Package.swift:**
```swift
dependencies: [
.package(url: "https://github.com/dynamic-labs/swift-sdk-and-sample-app.git", from: "1.0.2")
]
```
### ClientProps — All Fields Required
| 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"` |
**Dashboard:** 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://app.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` | All fields are required |
| 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 |
---
## 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.instance()` before initialization.
### 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 dashboard 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 dashboard.
### 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.
Install the SDK
Configure URL Scheme
Add a URL scheme to yourInfo.plist for authentication callbacks:Info.plist
<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>
redirectUrl you pass to ClientProps (for example yourapp://).Whitelist your mobile deep link
In the Dynamic dashboard, open your environment and go to Security → Whitelist Mobile Deeplink. Add your app’s deep link URL—the same value asredirectUrl (e.g. yourapp://). Without this, authentication callbacks and social login may not return to your app correctly.Initialize the SDK
Configuration Options
| Property | Description | Required |
|---|---|---|
environmentId | Your Dynamic environment ID from the dashboard | Yes |
appLogoUrl | URL to your app’s logo (shown in auth UI) | Yes |
appName | Your app’s display name | Yes |
redirectUrl | Deep link URL scheme for callbacks | Yes |
appOrigin | Your app’s origin URL | Yes |
logLevel | Logging level (.debug, .info, .warn, .error) | No |
debug | Debug options like ClientDebugProps(webview: true) | No |
Access the SDK
After initialization, access the SDK singleton anywhere in your app:let sdk = DynamicSDK.instance()
Basic Usage
Using Built-in Authentication UI
The easiest way to add authentication is using the built-in UI:import SwiftUI
import DynamicSDKSwift
struct ContentView: View {
var body: some View {
VStack {
Button("Sign In") {
DynamicSDK.instance().ui.showAuth()
}
}
}
}
Listening for Authentication State
Use Combine publishers to react to authentication changes: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 = DynamicSDK.instance()
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
import SwiftUI
import DynamicSDKSwift
import Combine
struct HomeView: View {
@State private var wallets: [BaseWallet] = []
@State private var cancellables = Set<AnyCancellable>()
private let sdk = DynamicSDK.instance()
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 Dashboard
Before you can use authentication and wallet features, enable them in your Dynamic dashboard:- Go to your Dynamic Dashboard
- Select your project
- Go to Authentication and enable the methods you want to use:
- Email OTP
- SMS OTP
- Social providers (Apple, Google, Farcaster)
- Passkeys
- Go to Wallets and enable embedded wallets
- Go to Chains and enable the networks you want to support (EVM and/or Solana)
- Go to Security → Whitelist Mobile Deeplink and add your app’s deep link URL (the same value as
redirectUrl, e.g.yourapp://)
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’sinit()before any views access the SDK - Don’t call
DynamicSDK.instance()before initialization
- Ensure
-
Authentication callbacks not working
- Verify your URL scheme is configured in
Info.plist - Ensure the
redirectUrlmatches your URL scheme - Whitelist your deep link URL in the Dynamic dashboard under Security → Whitelist Mobile Deeplink
- Verify your URL scheme is configured in
-
Wallets not appearing after login
- Wallets are created asynchronously after authentication
- Use
sdk.wallets.userWalletsChangespublisher to listen for wallet updates - Check that embedded wallets are enabled in your dashboard
-
Session state not updating
- Make sure you’re observing the reactive Combine publishers (
authenticatedUserChanges,userWalletsChanges, etc.) - Verify that your views are using
@StateObjector@ObservedObjectfor view models that observe SDK state
- Make sure you’re observing the reactive Combine publishers (
What’s Next
Great! Your SDK is now configured and ready to use. Here’s what you can do next:- Authentication Guide - Learn how to implement user authentication with email, SMS, and passkeys
- Social Authentication - Add social login options like Apple, Google, and Farcaster
- Wallet Operations - Work with balances and sign messages
- Networks - Configure blockchain networks