SDK Setup

Requirements, installation, environments and the credential provider for the iOS SDK.

Before You Start

Read the following guides before proceeding:

GuideWhy
iOS SDKSurfaces, isolation model and flows
AuthenticationThe user token accessToken() returns
Api BasicsX-Chain-Id and the identity headers the SDK sends

Requirements

RequirementValue
iOS15 or later — the framework's deployment target
Xcode26.5 or later. The binary ships a .swiftinterface that only the compiler that built it, or a newer one, can read
Package managerSwift Package Manager or CocoaPods

Install

Swift Package Manager

In Xcode, File › Add Package Dependencies… with https://github.com/wirexpay/baas-mobile-ios-sdk, or in
Package.swift:

.package(url: "https://github.com/wirexpay/baas-mobile-ios-sdk.git", from: "0.3.15")

Add the WirexMobileSDK product to your app target.

CocoaPods

pod 'WirexMobileSDK', '~> 0.3.15'

Both install the same WirexMobileSDK.xcframework attached to the release of each version. Swift Package
Manager verifies it against the checksum in Package.swift; CocoaPods against the sha256 in the podspec.


Integration Values

ValueWhat it isWhere it goes
testModefalse — production, true — the Wirex test environment. Used by the card surfacesWirexReveal.create, WirexCollect.create, WirexThreeDS(testMode:)
Environment nameThe same choice by name: "production" or "test", resolved with WirexWalletEnvironment.resolve(_:). Used by the walletWirexWalletConfig(environment:)
chainIdThe chain your integration operates on, sent as X-Chain-Id on every authenticated call. Required for Reveal and CollectchainId: on create(...)
clientIdOptional, wallet only. Selects your branding on the Wirex-hosted checkout pages. Leave nil unless Wirex assigned one. It is not a credentialWirexWalletConfig(clientId:)

The SDK holds the environment hosts. No public API takes a URL. An unknown environment name is an error — the
SDK never falls back to another environment.

Trap: WirexReveal.create(...) and WirexCollect.create(...) return nil — they do not throw — when
the build has no endpoints for the environment or chainId is empty. Call
WirexEnvironment.resolve(_:chainId:) to get the reason.


Credential Provider

The SDK never issues credentials. You implement WirexCredentialProvider; the SDK calls it when it needs a
token, so tokens are never pushed in advance.

import WirexMobileSDK

final class AppCredentials: WirexCredentialProvider {
    var userEmail: String? { nil }
    var userWallet: String? { "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18" }

    func accessToken() async throws -> String {
        try await session.freshAccessToken()
    }

    func actionToken(scope: WirexActionScope) async throws -> String {
        guard try await promptStepUp(scope) else { throw WirexDeclined() }
        return try await backend.mintActionToken(scope)
    }
}
MemberReturnsUsed by
accessToken()The user token from User Token Issuance (Login as User), sent as Authorization: BearerReveal, Collect
actionToken(scope:)An action_token for scope: .cardData (PAN, expiry, CVV) or .pinReveal
userEmailSent as X-User-EmailReveal, Collect
userWalletSent as X-User-WalletReveal, Collect
  • Exactly one identity. Return userEmail or userWallet, never both. Both set is a 400 from the API.
  • The SDK sends the bearer as-is and never retries a 401. Check the token's expiry inside
    accessToken() and refresh before returning. A stale token surfaces as WirexError with code
    unauthorized.
  • Both action scopes are answered with a GetCardDetails action token. Mint it through
    POST /api/v1/confirmation/signature/verify or the SMS OTP flow in
    Card Details. The token is valid for 5 minutes.
  • Throw WirexDeclined() from actionToken(scope:) when the user backs out. The SDK stops the operation and
    reports it as declined, not as an error.
  • Values are read per call. A refreshed token, a sign-out and a sign-in take effect on the next SDK
    request. Keep the provider reading live state rather than a captured copy — no reconfiguration is needed
    when the user changes.

Configure Once Per Process

  • Create WirexReveal, WirexCollect and WirexThreeDS once and keep them; clear the cached instance when
    create(...) returns nil, so a later attempt retries.
  • Derive testMode from your runtime environment when your app switches backends at runtime.
  • Log at debug in development builds and warn in release — see
    Logging.

Screen Protection

Revealed values and the card form are drawn in a capture-proof layer. The iOS Simulator renders that layer
blank. The 3-D Secure challenge is covered while the screen is recorded or mirrored.

Important: dangerouslyDisableScreenProtection: true on create(...) — and on
WirexThreeDS(dangerouslyDisableScreenProtection:) for the challenge — turns this off for Simulator
development and automated UI tests. It defeats PCI screen-capture protection. Never ship it enabled.


Quickstart

One credential provider, both card surfaces created once, a top-up card collected and an issued card
revealed. Each surface is covered in detail on its own page.

import UIKit
import WirexMobileSDK

protocol Auth: AnyObject {
    func freshAccessToken() async throws -> String
    func confirmStepUp() async -> Bool
    func mintActionToken() async throws -> String   // GetCardDetails, via POST /api/v1/confirmation/signature/verify
    var walletAddress: String { get }
}

final class AppCredentials: WirexCredentialProvider {
    private let auth: Auth
    init(auth: Auth) { self.auth = auth }

    var userEmail: String? { nil }
    var userWallet: String? { auth.walletAddress }

    func accessToken() async throws -> String { try await auth.freshAccessToken() }

    func actionToken(scope: WirexActionScope) async throws -> String {
        guard await auth.confirmStepUp() else { throw WirexDeclined() }
        return try await auth.mintActionToken()
    }
}

final class CardsViewController: UIViewController {
    var auth: Auth!
    var topUp: ((String) async throws -> Void)!   // your backend: estimate + execute with external_card_id
    private var form: WirexCollectForm?
    private var session: WirexRevealSession?

    override func viewDidLoad() {
        super.viewDidLoad()
        let credentials = AppCredentials(auth: auth)
        guard let collect = WirexCollect.create(credentials: credentials, testMode: true, chainId: "<chain id>"),
              let reveal = WirexReveal.create(credentials: credentials, testMode: true, chainId: "<chain id>") else {
            return   // no endpoints for this environment, or empty chainId
        }

        let form = collect.form(profile: .topup)
        view.addSubview(form)
        form.frame = CGRect(x: 16, y: 100, width: view.bounds.width - 32, height: form.naturalHeight)
        self.form = form

        if let session = try? reveal.session(cardId: "<card id>", fields: [.pan], cardholderName: "ALEX GREY") {
            let panRow = WirexRevealFieldView()
            panRow.bind(session: session, field: .pan)
            view.addSubview(panRow)
            panRow.frame = CGRect(x: 16, y: 100 + form.naturalHeight + 16, width: view.bounds.width - 32, height: panRow.naturalHeight)
            self.session = session
            Task { try await session.load() }
        }
    }

    func addCard() {
        guard let form else { return }
        Task { @MainActor in
            let token = try await form.tokenizeTopUp(WirexTopUpTokenizeParams(
                billingDetails: WirexBillingDetails(line1: "1 High Street", zipCode: "N1 9GU", city: "London", country: "GB")))
            try await topUp(token.cardId)
        }
    }
}

Did this page help you?