The Para SDK for Swift helps you integrate secure wallet features (creation, passkey-based authentication, transaction signing) into your iOS app. Below is an expanded guide that:

  • Describes environment setup for associated domains and passkeys
  • Shows how to create and log in users via email, phone, or OAuth
  • Demonstrates the final steps of wallet creation and basic message signing

Prerequisites

To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration.

Don’t have an API key yet? Request access to the Developer Portal to create API keys, manage billing, teams, and more.

Installation and Configuration

1

Add the Para Swift SDK Package

  1. In your Xcode project, go to File > Add Packages or (in your target) Frameworks, Libraries, and Embedded Content and click +.
  2. Enter https://github.com/getpara/swift-sdk
  3. Select Up to Next Major Version ensuring 0.0.1 < 1.0.0
  4. Add the package to your app target and click Add Package.
2

Associated Domains for Passkeys

To enable passkeys on iOS, you need to configure Associated Domains:

  • In Xcode, go to Signing & Capabilities and add Associated Domains.
  • Add:
    webcredentials:app.getpara.com
    webcredentials:app.beta.getpara.com
    webcredentials:app.sandbox.getpara.com
    
  • Provide your Team ID + Bundle ID to Para, e.g. A1B2C3D4E5.com.example.yourapp.
Without properly registering your domain with Para, passkey authentication flows will not succeed.

Beta Testing Credentials In the BETA Environment, you can use any email ending in @test.getpara.com (like dev@test.getpara.com) or US phone numbers (+1) in the format (area code)-555-xxxx (like (425)-555-1234). Any OTP code will work for verification with these test credentials. These credentials are for beta testing only. You can delete test users anytime in the beta developer console to free up user slots.

Initializing the Para Manager

Below is a typical SwiftUI @main App demonstrating how to load environment and API keys from Info.plist, then set up Para:

import SwiftUI
import ParaSwift

@main
struct exampleApp: App {
    @StateObject private var paraManager: ParaManager
    @StateObject private var appRootManager = AppRootManager()

    init() {
        let environmentString = Bundle.main.object(forInfoDictionaryKey: "PARA_ENVIRONMENT") as? String ?? "beta"
        let environment: ParaEnvironment = environmentString == "sandbox" ? .sandbox : .beta
        let apiKey = Bundle.main.object(forInfoDictionaryKey: "PARA_API_KEY") as! String

        _paraManager = StateObject(wrappedValue: ParaManager(environment: environment, apiKey: apiKey))
    }

    var body: some Scene {
        WindowGroup {
            switch appRootManager.currentRoot {
            case .authentication:
                // If user not authenticated, show sign-in flows
                UserAuthView()
                    .environmentObject(paraManager)
                    .environmentObject(appRootManager)
            case .home:
                // If user is authenticated, show their wallet
                WalletView()
                    .environmentObject(paraManager)
                    .environmentObject(appRootManager)
            }
        }
    }
}

Authentication Flows

Passkeys let you authenticate users with either email, phone (OTP-based), or OAuth. Each flow shares a common pattern:

  1. Check if user exists (by email or phone)
  2. Create user if new, which triggers OTP
  3. Verify OTP and generate passkey
  4. Create wallet to store user’s assets

Use the same ParaManager instance for each step. The code below outlines email-based creation. Phone and OAuth are structurally similar but call their respective methods (createUserByPhone, verifyByPhone, generatePasskey(identifier:...), etc.).

1

Check User Existence

let userExists = try await paraManager.checkIfUserExists(email: userEmail)
if userExists {
    // Prompt user to log in with passkey
}
2

Create User & Verify OTP

// If user does not exist, create user -> triggers OTP
try await paraManager.createUser(email: userEmail)

// Then call verify with the code user receives let biometricsId = try await paraManager.verify(verificationCode:
otpCode)

3

Generate Passkey & Create Wallet

// Associate passkey with the verified user
try await paraManager.generatePasskey(identifier: userEmail, biometricsId: biometricsId)

// Optionally create a wallet
try await paraManager.createWallet(skipDistributable: false)

Logging In Existing Users

Once the user has a passkey (they’ve previously created or logged in), logging in is straightforward:

try await paraManager.login(authorizationController: authorizationController)

This triggers a biometric prompt, verifying the passkey before unlocking the wallet. On success, you can fetch wallets or sign transactions.

Signing Messages or Transactions

You can sign arbitrary messages (e.g., EVM or Solana transactions) by passing Base64-encoded bytes. For instance:

let messageData = "Hello Solana".data(using: .utf8)!
let base64Message = messageData.base64EncodedString()
let signature = try await paraManager.signMessage(walletId: wallet.id, message: base64Message)

For complex transactions (EVM, Solana, Cosmos, etc.), ensure you serialize them properly and only then call signMessage.

Example Project

For more complete usage patterns, including a SwiftUI app demonstrating email, phone, and OAuth flows, as well as passkey creation and transaction signing:

Integration Support

If you’re experiencing issues that aren’t resolved by our troubleshooting resources, please contact our team for assistance. To help us resolve your issue quickly, please include the following information in your request:

  1. 1

    A detailed description of the problem you’re encountering.

  2. 2

    Any relevant error messages or logs.

  3. 3

    Steps to reproduce the issue.

  4. 4

    Details about your system or environment (e.g., device, operating system, software version).

Providing this information will enable our team to address your concerns more efficiently.