iOS SDK (v2.0+)
The AeroSync iOS SDK is a native SwiftUI SDK for secure bank account linking. Built with SwiftUI and WKWebView, it lets users connect their bank accounts through fast, tokenized connections — with full OAuth and MFA support. Your users' banking credentials are never shared with or stored by your application.
Looking for the old (v1.2.1) API?This page documents SDK v2.2.1. If your app still depends on an older
aerosync-ios-sdkinstall (installed via "Branch: main" rather than a version tag), see the iOS SDK (Legacy v1.2.1) page for that API reference, or jump to Migrating from v1.x below to upgrade.
Create a User
The Aerosync+Aeronetwork SDKs require a user identifier, aeroPassUserUuid, to be passed to the widget on launch. Follow the guide here to create a new user and access the user's aeroPassUserUuid.
Features
- Full bank linking experience — complete OAuth and credential-based flows via
AerosyncSDK - OAuth support — external browser opens automatically; returns cleanly to your app via deep link
- MFA support — multi-factor authentication flows with job and connection ID handling
- Multi-Account Linking: Return multiple linked accounts from a single flow (v2.2.0+)
- Swipe navigation — swipe right/left to go back and forward within the widget
- Theming — light and dark mode support
- Swift Package Manager — no CocoaPods required
Requirements
| Requirement | Minimum |
|---|---|
| iOS | 14.0+ |
| Xcode | 12.0+ |
| Swift | 5.3+ |
Installation
Swift Package Manager
In Xcode: File → Add Package Dependencies, then enter:
https://github.com/Aeropay-inc/aerosync-ios-sdk
Select version 2.2.1 or later, then click Add Package.
Or via Package.swift:
dependencies: [
.package(url: "https://github.com/Aeropay-inc/aerosync-ios-sdk", from: "2.2.1")
]Import
import aerosync_ios_sdkSetup
1. Register a URL scheme
OAuth bank flows redirect back to your app using a custom URL scheme. Add it to your app's Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>yourapp</string>
</array>
</dict>
</array>
Choose a unique scheme such asyourapporcom.yourcompany.yourapp. The full deeplink passed to the SDK would then beyourapp://connect.
2. Handle the deep link callback
In your SwiftUI App, add an onOpenURL modifier so returning from OAuth completes correctly:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
// The SDK resolves the OAuth return automatically.
// Add any additional routing logic here if needed.
}
}
}
}Using SceneDelegate instead?
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else { return }
// Handle url
}Quick Start
The minimum setup to get the bank linking widget running:
import SwiftUI
import aerosync_ios_sdk
struct BankLinkView: View {
var body: some View {
NavigationView {
AerosyncSDK(
token: "your-token-here",
env: "sandbox",
deeplink: "yourapp://connect",
aeroPassUserUuid: "user-uuid-123",
onEvent: { _ in },
onSuccess: { data in print(data) },
onClose: { _ in },
onLoad: { _ in },
onError: { _ in }
)
}
}
}
To generate a token, see the AeroSync integration guide.
Usage
Standard bank linking
import SwiftUI
import aerosync_ios_sdk
struct BankLinkView: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
AerosyncSDK(
token: "your-token-here",
env: "sandbox", // "sandbox" or "production"
deeplink: "yourapp://connect",
aeroPassUserUuid: "user-uuid-123", // Required
configurationId: "your-config-id", // Optional
theme: "light", // "light" or "dark"
manualLinkOnly: false,
handleMFA: false,
onEvent: { data in
// Widget events and page navigation signals
},
onSuccess: { data in
// Bank connection completed — parse data for credentials
print("Success: \(data)")
},
onClose: { _ in
presentationMode.wrappedValue.dismiss()
},
onLoad: { _ in
// Widget finished loading
},
onError: { error in
print("Error: \(error)")
}
)
.navigationTitle("Link Your Bank")
}
}
}MFA flow
Use this when re-authenticating an existing bank connection that requires multi-factor verification:
AerosyncSDK(
token: "your-token-here",
env: "sandbox",
deeplink: "yourapp://connect",
aeroPassUserUuid: "user-uuid-123",
handleMFA: true,
jobId: "your-job-id", // Required when handleMFA is true
connectionId: "your-conn-id", // Required when handleMFA is true
onEvent: { _ in },
onSuccess: { data in
// MFA completed successfully
},
onClose: { _ in },
onLoad: { _ in },
onError: { error in
print("MFA error: \(error)")
}
)Presenting as a sheet
struct ContentView: View {
@State private var showWidget = false
var body: some View {
Button("Link Bank Account") {
showWidget = true
}
.sheet(isPresented: $showWidget) {
NavigationView {
AerosyncSDK(
token: "your-token-here",
env: "sandbox",
deeplink: "yourapp://connect",
aeroPassUserUuid: "user-uuid-123",
onEvent: { _ in },
onSuccess: { data in
showWidget = false
},
onClose: { _ in
showWidget = false
},
onLoad: { _ in },
onError: { _ in }
)
.navigationTitle("Link Your Bank")
.navigationBarTitleDisplayMode(.inline)
}
}
}
}API Reference
To generate a token, see the AeroSync integration guide.
AerosyncSDK parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
token | String | Yes | — | Authentication token for the session |
env | String | Yes | — | "sandbox" or "production" |
deeplink | String | Yes | — | Full URL scheme for the OAuth callback (e.g. "yourapp://connect") |
aeroPassUserUuid | String | Yes | — | AeroPass user UUID — see Create a User |
configurationId | String? | No | nil | Configuration ID for widget customization |
theme | String | No | "light" | "light" or "dark" |
manualLinkOnly | Bool | No | false | Restrict to manual (non-OAuth) credential linking only |
handleMFA | Bool | No | false | Enable MFA re-authentication flow |
jobId | String? | No | nil | Required when handleMFA is true |
connectionId | String? | No | nil | Required when handleMFA is true |
Callbacks
| Callback | Signature | Description |
|---|---|---|
onSuccess | (String) -> Void | Bank connection completed — receives a JSON string (single account, or multiple accounts when the merchant's configuration has multi-account linking enabled; see Success Response) |
onClose | (Any) -> Void | User dismissed the widget |
onEvent | (Any) -> Void | Widget page events and navigation signals |
onError | (Any) -> Void | An error occurred during the flow |
onLoad | (Any) -> Void | Widget finished loading |
Success Response Format
The onSuccess callback receives a JSON string:
{
"type": "pageSuccess",
"payload": {
"connectionId": "33e1121dde934c5cb5b964b325e28728",
"aeroPassUserUuid": "b120bcb5-2f39-48d3-a654-1c437c1ec175",
"clientName": "Aeropay"
}
}| Field | Description |
|---|---|
connectionId | Unique identifier for the bank connection |
aeroPassUserUuid | AeroPass UUID for the authenticated user |
clientName | Your registered client name on the AeroSync platform |
Multi-Account Linking (v2.2.0+)
If your AeroSync configuration has multi-account linking enabled, the same onSuccess event instead carries a list of linked accounts:
{
"type": "pageSuccess",
"payload": {
"accounts": [
{ "connectionId": "33e1121dde934c5cb5b964b325e28728", "accountType": "checking", "accountNumberDisplay": "••••1234" },
{ "connectionId": "84f2232eef045d6dc6a075436f39839", "accountType": "savings", "accountNumberDisplay": "••••5678" }
],
"clientName": "Aeropay",
"aeroPassUserUuid": "b120bcb5-2f39-48d3-a654-1c437c1ec175"
}
}| Field | Description |
|---|---|
accounts | List of linked accounts (multi-account only) — each has connectionId, accountType, accountNumberDisplay |
Multi-account linking is opt-in and configured server-side on your AeroSync configuration — no extra widget parameters are required to receive it. onSuccess keeps its existing (String) -> Void signature either way, so adopting the typed models below is optional and non-breaking.
Parsing with the typed models:
onSuccess: { message in
guard let result = AerosyncSuccessPayload.parse(from: message) else { return }
switch result {
case .multiAccount(let payload):
for account in payload.accounts {
print("\(account.accountType): \(account.accountNumberDisplay)")
}
case .singleAccount(let payload):
print("Connected: \(payload.connectionId)")
}
}Manual parsing (if you'd rather not use the typed models):
onSuccess: { data in
guard let jsonData = data.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
let payload = json["payload"] as? [String: Any] else { return }
if let accounts = payload["accounts"] as? [[String: Any]] {
// Multi-account: each entry has connectionId, accountType, accountNumberDisplay
} else {
let connectionId = payload["connectionId"] as? String
// Use this to authenticate with the AeroSync API
}
}
Naming noteThe typed models use the
Aerosyncprefix (AerosyncSuccessPayload,AerosyncSingleAccountSuccessPayload,AerosyncMultiAccountSuccessPayload,AerosyncLinkedAccount) — notAeroSync. This differs slightly from the Flutter and React Native SDKs' equivalent types, so don't cross-copy the casing between platforms.
What's New Across 2.x
- v2.1.0 — removed
AerosyncEmbeddedView(to be re-introduced in a future release) and removed all debugprintstatements from production code paths - v2.2.0 — added the typed multi-account success models described above
- v2.2.1 — fixed the
sandboxenvironment host (https://sandbox.aerosync.com→https://sandbox-sync.aero.inc) — if you allowlist AeroSync domains in a firewall or CSP policy, update it to the new host
Migrating from 1.x
SDK 2.x introduced several breaking changes. If you're upgrading from an app installed via "Branch: main" (pre-2.0), update your integration as follows:
| 1.x | 2.x | Notes |
|---|---|---|
| Install via Xcode "Branch: main" | Install via version tag (from: "2.2.1") | Pin to a real release instead of tracking main |
consumerId | configurationId | Parameter renamed |
userId (MFA) | connectionId (MFA) | MFA parameter renamed — used together with jobId and handleMFA: true |
| — | aeroPassUserUuid | New required parameter — must be added to every AerosyncSDK usage. See Create a User to generate one. |
env values: staging, production (and dev per some 1.x releases) | env values: sandbox, production only | dev/staging no longer supported — any other value force-crashes |
| — | manualLinkOnly | New parameter to restrict to manual (non-OAuth) linking only |
Success payload: {ClientName, user_id, user_password, FILoginAcctId} | Success payload: {connectionId, clientName, aeroPassUserUuid} (or {accounts: [...], clientName, aeroPassUserUuid} for multi-account) | Field names and shape changed |
AerosyncEmbeddedView | (removed in 2.1.0) | Will be re-introduced in a future release |
See the iOS SDK (Legacy v1.2.1) page for the full old API reference while you migrate.
Troubleshooting
Widget not loading
- Verify your token is valid and has not expired
- Confirm the
envvalue is either"sandbox"or"production"— any other value will crash with a force-unwrap
OAuth browser never opens
- Confirm
javaScriptCanOpenWindowsAutomaticallyis not blocked by any customWKWebViewConfigurationlayer in your app — the SDK sets this totrueinternally, so ensure no parent configuration overrides it
App does not return after OAuth
- Verify the URL scheme in
Info.plistmatches the scheme in yourdeeplinkparameter exactly (e.g. ifdeeplinkisyourapp://connect, the scheme must beyourapp) - Confirm your
ApporSceneDelegatehas theonOpenURL/openURLContextshandler wired up
onSuccess never fires
onSuccess never fires- Check that your bank linking flow fully completes — partial flows (user closes early) trigger
onClose, notonSuccess - Inspect raw events in
onEventto trace the widget state
Build errors after upgrading
- Run File → Packages → Update to Latest Package Versions in Xcode
- Ensure the deployment target is iOS 14.0+
- Clean the build folder: Product → Clean Build Folder (⇧⌘K)
License
This SDK is proprietary software. Please refer to your license agreement for usage terms.
Updated about 1 month ago