iOS SDK

The three screens the SDK presents: choosing a recipient, entering an amount, and confirming.

The iOS SDK lets your customers send money to anyone from inside your app. You launch it from a single call site, it runs full screen, and control returns to you when the user is done.

You supply a short-lived token from your backend and your brand color. The SDK resolves the user’s funding methods itself and handles the rest. The other person claims or pays on the hosted page.

Requirements #

  • iOS 18.0 or later
  • Xcode 26.0 or later; earlier toolchains can’t compile the framework’s interface
  • Swift 6.0 language mode

Add the package #

The SDK resolves from a private GitHub repository, so Xcode needs a credential before it can fetch anything. Moov supplies a dedicated GitHub account for your team.

  1. Sign in to the GitHub account Moov gave you and create a personal access token with read access to repository contents.

  2. Add that account to Xcode under Settings → Accounts, using the token as the password.

  3. Open File → Add Package Dependencies and enter the package URL:

    https://github.com/moov-sdk/moov-money-ios.git
    

    Set the dependency rule to Up to Next Minor Version. The SDK is pre-1.0, so minor versions can contain breaking changes. Pinning to the minor keeps an upgrade a deliberate act.

  4. Add the MoovMoney library to your app target.

If you resolve packages from a Package.swift instead:

dependencies: [
    .package(
        url: "https://github.com/moov-sdk/moov-money-ios.git",
        .upToNextMinor(from: "0.1.0")
    ),
]

Adding the MoovMoney library to your target embeds and signs everything the SDK needs into your app bundle. There’s nothing further to configure.

On CI, Xcode’s account store doesn’t exist, so supply the credential one of two ways. Use the same token through a git credential helper, which works with the https:// URL above:

git config --global credential.helper store
printf 'https://x-access-token:%[email protected]\n' "$GITHUB_TOKEN" > ~/.git-credentials

Or use an SSH deploy key. Change the package URL to [email protected]:moov-sdk/moov-money-ios.git and pass -scmProvider system to xcodebuild so it uses the system git and your SSH agent rather than its own client.

Add usage descriptions #

The SDK needs three usage description strings in your app’s Info.plist. Without the first, the SDK won’t launch. Without the third, your App Store upload is rejected.

<key>NSFaceIDUsageDescription</key>
<string>Confirm it's you before sending a payment.</string>

<key>NSContactsUsageDescription</key>
<string>Find friends and family in your address book to pay.</string>

<key>NSCameraUsageDescription</key>
<string>Take a new profile photo.</string>

NSFaceIDUsageDescription lets the SDK confirm the device owner’s presence before submitting a payment. iOS terminates apps that reach LocalAuthentication without this key, so the SDK refuses to launch instead of crashing your app. You’ll see this as the missingEntitlement case in the next step.

NSContactsUsageDescription lets the SDK offer the device address book when the user chooses a recipient.

NSCameraUsageDescription is required even if you never show a camera interface. The SDK’s binary references AVCaptureDevice, and Apple’s static scan rejects the upload with ITMS-90683 when the string is absent. There’s no way to opt out of this one.

Check availability #

Consult MoovMoneySDK.availability before you offer your entry point. This is part of the integration contract rather than an optimization: it’s what lets you route to a web fallback instead of showing a button that can’t work.

import MoovMoney
import SwiftUI

struct SendMoneyButton: View {
    let configuration: MoovMoneyConfiguration
    let accessToken: String

    @State private var availability: MoovMoneyAvailability?
    @State private var isPresented = false

    var body: some View {
        Button("Send money") {
            if case .unavailable = availability {
                showWebFallback()
            } else {
                isPresented = true
            }
        }
        .onAppear { availability = MoovMoneySDK.availability }
        .moovMoney(
            isPresented: $isPresented,
            accessToken: accessToken,
            configuration: configuration
        )
    }

    private func showWebFallback() { /* your existing web experience */ }
}

Read the verdict once and hold it, as above. Every .unavailable evaluation emits a diagnostic log line with no deduplication, so calling it from inside a SwiftUI body re-emits on every view update.

To tell the reasons apart, for example to distinguish an integration mistake from an old device:

if case .unavailable(let reason) = MoovMoneySDK.availability {
    switch reason {
    case .unsupportedOSVersion(let required):
        // The device is below the minimum iOS version, carried in `required`.
        // Offer your web experience.
        break
    case .missingEntitlement(let key):
        // Your Info.plist is missing `key`. See the previous step.
        break
    case .configurationInvalid(let detail):
        // `detail` names what is wrong with the configuration.
        break
    @unknown default:
        // Treat an unrecognized reason as unavailable and fall back.
        break
    }
}

availability is advisory. Both presentation entry points enforce the same checks independently, so a launch that shouldn’t happen presents nothing and logs it, rather than showing a broken experience.

Build a configuration #

Every field has a default, so the smallest usable configuration is empty. The SDK fetches the signed-in user’s eligible funding methods itself, scoped from the access token: the ones you registered against the participant. There’s nothing for you to resolve and pass in.

let configuration = MoovMoneyConfiguration()

To brand the experience and receive the SDK’s logs:

let configuration = MoovMoneyConfiguration(
    colors: MoovMoneyColors(primary: myBrandColor),
    logger: MoovMoneyLogger { entry in
        myLogger.log("[\(entry.category)] \(entry.message)")
    }
)

Wire up the logger. The SDK reports no outcome to your code, so these lines are the only signal your integration gets, including when the SDK refuses to launch. See Diagnostics below.

Present the experience #

You pass the access token on each presentation rather than storing it in the configuration, so the SDK always runs against a freshly minted credential. It’s a short-lived OAuth bearer token from your own token exchange, and it’s opaque to the SDK, never decoded on device. Your session model stays the authority on who can send or request. See sender authentication for how to mint it.

Attach the moovMoney modifier and drive it with a Binding, as shown under Check availability. The SDK sets the binding back to false when the flow finishes. There’s no result to handle.

Button("Send money") { isPresented = true }
    .moovMoney(
        isPresented: $isPresented,
        accessToken: accessToken,
        configuration: configuration
    )

The SwiftUI path can’t lock orientation. The amount-entry keyboard is portrait-only, so lock orientation at the app level if you need it enforced.

Diagnostics #

The SDK returns nothing to your code: no return value, no callback, no thrown error. A MoovMoneyLogger on your configuration is the only channel by which your integration learns what the SDK is doing.

That includes the launches it refuses. Nothing is presented and nothing is returned when:

  • the view controller you presented from is already presenting something
  • the access token is blank
  • the device fails the availability checks

Each entry carries a level (.debug, .info, or .error), a category naming the subsystem ("SDK" for most lines, but also "Contacts" and "Payouts"), a message, and an optional underlying error. Entries are Sendable, so you can forward one into a Task, buffer it, or ship it to a crash reporter asynchronously.

let appLog = Logger(subsystem: "com.example.bank", category: "MoovMoney")

let configuration = MoovMoneyConfiguration(
    logger: MoovMoneyLogger { entry in
        appLog.log("[\(entry.category)] \(entry.message)")
    }
)

It’s safe to leave installed in production. Values the SDK treats as sensitive are rendered <redacted> in message before they reach you, so no account number, phone number, amount, URL, response body, or identifier appears in that string. The error field is passed through as-is and carries no such guarantee.

Messages, categories, and levels are debugging aids rather than a stable contract, and they change between releases. Don’t build alerting or parsing on them.

Theming #

MoovMoneyColors has one slot by design:

MoovMoneyColors(primary: .indigo)

The SDK owns the rest of its design language, drawing neutrals from iOS system semantic colors so they adapt to light and dark, Increase Contrast, and Smart Invert. primary is only ever used as a fill, and the content drawn on it is derived per appearance, so a static Color and an adaptive one are both safe. Omit it to use the SDK’s own default.

Disclose data collection #

The SDK ships a privacy manifest that merges into your app’s privacy report, so these entries appear in your App Store submission. Disclose them accordingly.

Data typeLinked to userTrackingPurpose
ContactsYesNoApp functionality
Photos or VideosYesNoApp functionality

Contacts covers the user’s address book, which the SDK uses to match contacts to Moov Money participants so the user can find and pay people they know.

Photos or Videos covers the profile photo a user chooses, which is stored against their account and used to render their avatar.

The manifest also declares the device and behavioral signals the SDK collects during the payment flow for fraud prevention. Those entries appear in your privacy report alongside the two above. The SDK performs no tracking as Apple defines it, and declares no tracking domains.

Known limitation #

If your app already integrates a fraud or device intelligence SDK of its own, you may hit a build-time collision when you add Moov Money. It surfaces immediately and loudly, either at package resolution or as a duplicate framework error, so it can’t ship by accident.

There’s no workaround to apply on your side. Contact your Moov integration contact and we’ll sort out the packaging with you.

Next steps #

  • Every public symbol carries documentation that ships inside the framework. Option-click any MoovMoney type or method in Xcode for full Quick Help, with no setup and no hosted site required.
  • The SDK repo’s SampleApp target is a complete working integration.
  • See sender authentication for minting the access token.
  • The funding methods the SDK offers are the ones you registered against the participant. See create funding source and the ledger contract in the API reference.