iOS

The iOS SDK wraps the Lean Link SDK in a native WKWebView-backed view, with both SwiftUI and UIKit entry points.

📘

Prerequisites

Before calling any SDK method you need to have completed the backend setup in the Getting Started section:

This page covers only the client-side SDK integration.

Installation

Swift Package Manager (recommended)

In Xcode go to File → Add Package Dependencies and paste:

https://github.com/leantechnologies/link-sdk-ios-distribution

Manual xcframework

Download the .xcframework zip from the distribution Package.swift and add it to your target.

Latest version is listed on releases.

Import

import LeanSDK

Demo app

See link-ios-demo-application.

Setup

Configure the manager once during app launch (or before any SDK call):

Lean.manager.setup(
    appToken: "<YOUR_APP_TOKEN>",
    sandbox: true,
    version: "latest"
)

Usage — SwiftUI

Place Lean.manager.view inside a .fullScreenCover and toggle it via state.

import SwiftUI
import LeanSDK

@main
struct LeanTestApp: App {
    init() {
        Lean.manager.setup(
            appToken: "<YOUR_APP_TOKEN>",
            sandbox: true,
            version: "latest"
        )
    }
    var body: some Scene { WindowGroup { ContentView() } }
}

struct ContentView: View {
    @State private var isPresented = false

    var body: some View {
        Button("Connect", action: handleConnect)
            .fullScreenCover(isPresented: $isPresented) {
                Lean.manager.view.ignoresSafeArea()
            }
    }

    func handleConnect() {
        Lean.manager.connect(
            customerId: "CUSTOMER_ID",
            permissions: [.Identity, .Accounts, .Transactions, .Balance],
            paymentDestinationId: nil,
            bankId: nil,
            customization: nil,
            success: { isPresented = false },
            error: { status in
                print(status.status, status.message)
                isPresented = false
            }
        )
        isPresented = true
    }
}

Usage — UIKit

Call setup(...) in viewDidLoad, then pass a presentingViewController to each method. The SDK presents itself modally.

import UIKit
import LeanSDK

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        Lean.manager.setup(appToken: "<YOUR_APP_TOKEN>", sandbox: true, version: "latest")
    }

    @IBAction func handleConnect(_ sender: Any) {
        Lean.manager.connect(
            presentingViewController: self,
            customerId: "CUSTOMER_ID",
            permissions: [.Identity, .Accounts, .Transactions, .Balance],
            bankId: nil,
            customization: nil,
            success: { status in /* ... */ },
            error: { status in print(status) }
        )
    }
}

Available methods

Each method has a SwiftUI and a UIKit variant — the SwiftUI form omits presentingViewController. The UIKit variants accept presentingViewController as the first parameter.

.connect()

Link a bank account for data access (and optionally payments).

Required: customerId, permissions.

Lean.manager.connect(
    customerId: String,
    permissions: [LeanPermission],
    paymentDestinationId: String?,
    bankId: String?,
    customization: LeanCustomization?,
    accountType: String? = nil,
    endUserId: String? = nil,
    accessTo: String? = nil,
    accessFrom: String? = nil,
    failRedirectUrl: String? = nil,
    successRedirectUrl: String? = nil,
    accessToken: String? = nil,
    showConsentExplanation: Bool? = nil,
    destinationAlias: String? = nil,
    destinationAvatar: String? = nil,
    customerMetadata: String? = nil,
    success: () -> (),
    error: (LeanStatus) -> ()
)

.pay()

Initiate a bank-to-bank payment.

Required: exactly one of paymentIntentId or bulkPaymentIntentId.

Lean.manager.pay(
    paymentIntentId: String? = nil,
    bulkPaymentIntentId: String? = nil,
    accountId: String?,
    bankId: String? = nil,
    customization: LeanCustomization?,
    endUserId: String? = nil,
    failRedirectUrl: String? = nil,
    successRedirectUrl: String? = nil,
    accessToken: String? = nil,
    destinationAlias: String? = nil,
    destinationAvatar: String? = nil,
    riskDetails: LeanRiskDetails? = nil,
    success: () -> (),
    error: (LeanStatus) -> ()
)

.reconnect()

Restore a broken bank connection.

Required: reconnectId.

Lean.manager.reconnect(
    reconnectId: String,
    customization: LeanCustomization?,
    accessToken: String? = nil,
    destinationAlias: String? = nil,
    destinationAvatar: String? = nil,
    success: () -> (),
    error: (LeanStatus) -> ()
)

.updatePaymentSource()

Add a beneficiary to an existing payment source.

Required: customerId, paymentSourceId (or entityId), paymentDestinationId.

⚠️

The iOS signature marks paymentSourceId as non-optional. To upgrade a data-only customer with entityId on iOS today, set paymentSourceId to an empty string and provide entityId. This mismatch vs the Web SDK is tracked for a future release.

Lean.manager.updatePaymentSource(
    customerId: String,
    paymentSourceId: String,
    paymentDestinationId: String,
    customization: LeanCustomization?,
    endUserId: String? = nil,
    accessToken: String? = nil,
    entityId: String? = nil,
    destinationAlias: String? = nil,
    destinationAvatar: String? = nil,
    success: () -> (),
    error: (LeanStatus) -> ()
)

.checkout()

Open Finance guest payment — the customer authenticates at the bank and is redirected back to your successRedirectUrl / failRedirectUrl with status query parameters you feed into captureRedirect.

Required by the underlying SDK validator: paymentIntentId, successRedirectUrl, failRedirectUrl. The Swift signature accepts nil for the redirect URLs but they must be supplied for the flow to succeed at runtime.

Lean.manager.checkout(
    paymentIntentId: String,
    customization: LeanCustomization?,
    customerName: String? = nil,
    bankId: String? = nil,
    accessToken: String? = nil,
    successRedirectUrl: String? = nil,
    failRedirectUrl: String? = nil,
    riskDetails: LeanRiskDetails? = nil,
    success: () -> (),
    error: (LeanStatus) -> ()
)

.authorizeConsent()

Grant a long-lived Open Finance consent. Redirects to the bank and returns to your successRedirectUrl / failRedirectUrl.

Required: customerId, consentId, successRedirectUrl, failRedirectUrl.

Lean.manager.authorizeConsent(
    customerId: String,
    consentId: String,
    failRedirectUrl: String,
    successRedirectUrl: String,
    customization: LeanCustomization? = nil,
    accessToken: String? = nil,
    destinationAlias: String? = nil,
    destinationAvatar: String? = nil,
    riskDetails: LeanRiskDetails? = nil,
    success: () -> (),
    error: (LeanStatus) -> ()
)

.manageConsents()

Show a customer-facing consent management screen.

Required: customerId.

Lean.manager.manageConsents(
    customerId: String,
    customization: LeanCustomization?,
    accessToken: String? = nil,
    success: () -> (),
    error: (LeanStatus) -> ()
)

.captureRedirect()

Show the outcome screen after an Open Finance redirect. Extract the status query parameters Lean appends to your redirect URL (consentAttemptId, granularStatusCode, statusAdditionalInfo) and pass them in.

Required: customerId.

Lean.manager.captureRedirect(
    customerId: String,
    customization: LeanCustomization?,
    accessToken: String? = nil,
    consentAttemptId: String? = nil,
    granularStatusCode: String? = nil,
    statusAdditionalInfo: String? = nil,
    success: () -> (),
    error: (LeanStatus) -> ()
)

.verifyAddress()

Proof-of-address check.

Required: customerId, customerName, permissions (typically [.Identity]).

Lean.manager.verifyAddress(
    customerId: String,
    customerName: String,
    permissions: [LeanPermission],
    customization: LeanCustomization?,
    accessToken: String? = nil,
    destinationAlias: String? = nil,
    destinationAvatar: String? = nil,
    success: () -> (),
    error: (LeanStatus) -> ()
)

Deprecated methods

.createPaymentSource()

⚠️

Deprecated — use .connect() with permissions: [.Payments] instead.

Alias for connect that forces the payments permission. Retained for backwards compatibility.

Lean.manager.createPaymentSource(
    customerId: String,
    paymentDestinationId: String?,
    bankId: String? = nil,
    customization: LeanCustomization?,
    accessToken: String? = nil,
    destinationAlias: String? = nil,
    destinationAvatar: String? = nil,
    success: () -> (),
    error: (LeanStatus) -> ()
)

Callback

Success and error closures receive a LeanStatus object. Status codes documented at LinkSDK statuses.

Lean.manager.connect(
    customerId: "CUSTOMER_ID",
    permissions: [.Identity, .Accounts],
    paymentDestinationId: nil,
    bankId: nil,
    customization: nil,
    success: {
        // SUCCESS path
    },
    error: { status in
        print(status.status, status.message, status.method)
    }
)

Customisation

LeanCustomization wraps the same field set as the Web SDK's customization object in camelCase. See Customisation for every property.

let custom = LeanCustomization(
    dialogMode: "contained",
    themeColor: "#0080ff",
    buttonTextColor: "#ffffff",
    buttonBorderRadius: "8",
    linkColor: "#0080ff",
    overlayColor: "rgba(0, 0, 0, 0.8)"
)

Lean.manager.connect(
    customerId: "CUSTOMER_ID",
    permissions: [.Identity, .Accounts],
    paymentDestinationId: nil,
    bankId: nil,
    customization: custom,
    success: { /* ... */ },
    error: { _ in /* ... */ }
)

Language

Pass language: "ar" to Lean.manager.setup(...) for Arabic (right-to-left). Omit for English.

Bank-list and account-selection shortcuts

Pass bankId to skip the bank-selection screen (Lean bank identifier — see Create your own bank list). Pass accountId on pay to skip the payment-source-selection screen.

Related