Your team id can be found from Apple's developer portal in the top right corner of the Certificates, Identifiers & Profiles section.

Your bundle identifier can be found here

To get an API Key and configure your team and bundle ids, please go to the Developer Portal.
Once you've created an API key, please fill out the "Native Passkey Configuration" Section with your App Info described above. Please note that once entered, this information can take up to a day to be reflected by Apple. Ping us if you have any questions or if you would like to check in on the status of this
The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift
compiler.
Once you have your Swift package set up, adding ParaSwift as a dependency is as easy as adding it to the dependencies
value of your Package.swift
or the Package list in Xcode.
dependencies: [
.package(url: "https://github.com/getpara/swift-sdk.git", .upToNextMajor(from: "1.0.0"))
]
Normally you'll want to depend on the ParaSwift
target:
.product(name: "ParaSwift", package: "ParaSwift")
ParaSwift utilizes native passkeys for authentication and wallet information. In order to use native passkeys in your app you will need several things
Under Targets->AppName->Signing & Capabilities, click on the +Capability button.

From the prompt that appears, search for and select Associated Domains

Note: In order to add the associated domains capability to your project, you cannot use a personal team for the purposes of signing. If you are, you need to set up a company team with Apple.
In the associated domains section that appears after adding it, you will need to add two domains
- webcredentials:app.beta.usecapsule.com
- webcredentials:app.usecapsule.com


This will allow you to use passkeys that have been created on any app that uses the Capsule system, so if your users already have a Capsule wallet they will be able to use it in your app.
Under Targets->AppName->Info, click on URL Types
Add a new URL Type and set the URL Schemes value to whatever you would like to use for deeplinking. By default, the ParaManager uses the apps Bundle Identifier, but this can be manually set when instantiating the class.
This will allow you to use OAuth and Connectors with Para
ParaSwift provides an interface to Capsule services from within iOS applications using SwiftUI (Support for UIKit coming soon).
To configure the capsule instance, you will need to create an instance of the capsule object as well as the globally available authorizationController environment object. This will be needed in several functions later on. If you need an API Key, please reach out to the Capsule Team.
// Load Para configuration
let config = ParaConfig.fromEnvironment()
// Initialize Para manager
let paraManager = ParaManager(environment: config.environment, apiKey: config.apiKey)
Note: Make sure to configure your app's URL scheme in Info.plist under
CFBundleURLTypes
. ThedeepLink
parameter should match this URL scheme. By default, the deepLink parameter will be your Bundle Identifier.
To create a user, you should first check in the provided email address exists, and if it does not then create it
Button("Sign Up") {
Task.init {
let userExists = try! await paraManager.checkIfUserExists(email: email)
if userExists {
return
}
try! await paraManager.createUser(email: email)
...
}
}
Upon success, the user should receive an email with a 6 digit verification pin. Call the verify function with the verification code acquired from this step. This will return a biometricsId which will be necessary to pass to the next function, generatePasskey.
Generate passkey takes in the authorizationController that was set up earlier. This is necessary to be able to allow the application to present the Passkey modals for creating and selecting a Passkey.
After generating the passkey, the last step is to create a wallet.
Button("Verify") {
Task.init {
let biometricsId = try! await paraManager.verify(verificationCode: code)
try! await paraManager.generatePasskey(email: email, biometricsId: biometricsId, authorizationController: authorizationController)
try! await paraManager.createWallet(skipDistributable: false)
}
}
After the wallet has been created, it will be set in the ParaManager object as a Published var.
To sign a message, all you need to do is pass in the id of the wallet you would like to use which can be obtained from the paraManager.wallet property, and the text that you would like to sign. This will produce a messageSignature.
Button("Sign Message") {
Task.init {
let messageSignature = try! await paraManager.signMessage(walletId: wallet.id, message: "Some message to sign")
...
}
}
You may also use the evm signer to sign messages, sign transactions, and send transactions
To start, you must instantiate the signer with your preferred JsonRPCProvider URL. For this example, we are using Infura Sepolia and the first wallet in the paraManager. The wallet is optional, and can be selected / changed after instantiation.
let paraEvmSigner = try! ParaEvmSigner(paraManager: paraManager, rpcUrl: "https://sepolia.infura.io/v3/<YOUR_API_KEY>", walletId: paraManager.wallets.first!.id)
try! await paraEvmSigner.selectWallet(walletId: paraManager.wallets.first!.id)
let message = "Hello, World!"
let signature = try! await paraEvmSigner.signMessage(message)
print(signature)
To sign a transaction, you must first JSONEncode the transaction object, and then b64Encode the resulting data to get the base64 encoded string. This is what needs to be passed to the paraEvmSigner signTransaction function.
let transaction = Transaction(<TX_PARAMS>)
let encodedTransaction = try! JSONEncoder().encode(transaction)
let b64EncodedTransaction = encodedTransaction.base64EncodedString()
let signature = try! await paraEvmSigner.signTransaction(b64EncodedTransaction)
print(signature)
To send a transaction, you must first JSONEncode the transaction object, and then b64Encode the resulting data to get the base64 encoded string. This is what needs to be passed to the paraEvmSigner sendTransaction function.
let transaction = Transaction(<TX_PARAMS>)
let encodedTransaction = try! JSONEncoder().encode(transaction)
let b64EncodedTransaction = encodedTransaction.base64EncodedString()
let signedTx = try! await paraEvmSigner.sendTransaction(b64EncodedTransaction)
print(signedTx)
ParaSwift provides seamless integration with MetaMask Mobile through the MetaMaskConnector
class. This allows your iOS app to connect with MetaMask wallets, sign messages, and send transactions.
First, configure your app's Info.plist to allow querying MetaMask URL schemes:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>metamask</string>
</array>
This configuration is required to detect if MetaMask is installed on the device.
Then initialize the MetaMask connector with your app's configuration:
// Create MetaMask configuration
let bundleId = Bundle.main.bundleIdentifier ?? ""
let metaMaskConfig = MetaMaskConfig(
appName: "Your App Name",
appId: bundleId,
apiVersion: "1.0"
)
// Initialize the connector
let metaMaskConnector = MetaMaskConnector(
para: paraManager,
appUrl: "https://\(bundleId)", // Your app's URL
config: metaMaskConfig
)
Add deep link handling in your SwiftUI app's main entry point:
@main
struct YourApp: App {
@StateObject private var metaMaskConnector: MetaMaskConnector
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
// Handle MetaMask deep links
metaMaskConnector.handleURL(url)
}
}
}
}
Connect to MetaMask and get the user's accounts:
do {
try await metaMaskConnector.connect()
// Connection successful
// Access connected accounts via metaMaskConnector.accounts
} catch {
// Handle connection error
}
Request message signing from the connected MetaMask wallet:
guard let account = metaMaskConnector.accounts.first else { return }
do {
let signature = try await metaMaskConnector.signMessage(
"Message to sign",
account: account
)
// Use the signature
} catch {
// Handle signing error
}
Send transactions through the connected MetaMask wallet:
guard let account = metaMaskConnector.accounts.first else { return }
let transaction: [String: String] = [
"from": account,
"to": "0x...", // Recipient address
"value": "0x...", // Value in wei (hex)
"gasLimit": "0x..." // Gas limit (hex)
]
do {
let txHash = try await metaMaskConnector.sendTransaction(
transaction,
account: account
)
// Transaction sent successfully
} catch {
// Handle transaction error
}
The MetaMask connector provides several useful properties:
isConnected
: Boolean indicating if MetaMask is connectedaccounts
: Array of connected MetaMask account addresseschainId
: Current chain ID (e.g., "0x1" for Ethereum mainnet)