Skip to main content

Requirements

  • iOS 16 or higher
  • Swift 5.9+
  • SwiftUI

Installation

Add the Benny SDK as a Swift Package Manager dependency in Xcode:
  1. Go to File > Add Package Dependencies.
  2. Enter the repository URL: https://github.com/Benny-API/benny-mobile-sdk
  3. Select the BennySDK library product.
Package.swift
// Or in a Package.swift file:
dependencies: [
    .package(url: "https://github.com/Benny-API/benny-mobile-sdk", from: "0.1.0"),
],
targets: [
    .target(
        name: "YourApp",
        dependencies: [
            .product(name: "BennySDK", package: "benny-mobile-sdk"),
        ]
    ),
]
A single import BennySDK gives you access to all SDK types—no additional imports required.

Initialize the SDK

Call BennySDK.initialize() once at app startup, before rendering any SDK components. Your App.init() is the recommended place.
import SwiftUI
import BennySDK

@main
struct MyApp: App {
    init() {
        BennySDK.initialize(
            organizationId: "your-organization-id",
            environment: .sandbox // or .production (default)
        )
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
organizationId
String
required
Your Benny organization ID.
environment
BennySdk.BennySdkConfigEnvironment
default:".production"
.sandbox for testing, .production for live transactions.

Card Tokenization

The CardNumberInput view renders a secure card number field. Card data never touches your servers—it’s tokenized directly by the SDK.
import SwiftUI
import BennySDK

struct CardCollectionScreen: View {
    @State private var isLoading = false
    @State private var error: String?
    @State private var tokenId: String?
    @StateObject private var controller: CardNumberInputController

    init(sessionToken: String) {
        _controller = StateObject(
            wrappedValue: CardNumberInputController(btApiKey: sessionToken)
        )
    }

    var body: some View {
        VStack {
            CardNumberInput(
                controller: controller,
                onLoadingChange: { isLoading = $0 },
                onError: { error = $0 },
                onSuccess: { tokenId = $0 }
            )

            Button("Tokenize") {
                controller.tokenize()
            }
            .disabled(isLoading)

            if let error {
                Text(error).foregroundColor(.red)
            }
            if let tokenId {
                Text("Token: \(tokenId)")
            }
        }
    }
}

How it works

  1. Create a CardNumberInputController as a @StateObject, passing the session token from your backend.
  2. Place CardNumberInput in your view. It renders a secure text field for the card number.
  3. Call controller.tokenize() when the user is ready to submit.
  4. On success, onSuccess receives a PAN token ID (string) that you can send to your backend.

Callbacks

CallbackTypeDescription
onLoadingChange(Bool) -> VoidCalled with true when a request starts and false when it completes.
onError(String?) -> VoidCalled with an error message on failure, or nil to clear a previous error.
onSuccess(String) -> VoidCalled with the PAN token ID on successful tokenization.

PIN entry and EBT operations

The PinInput view renders a secure PIN field for EBT balance checks and payments.
import SwiftUI
import BennySDK

struct PinEntryScreen: View {
    let panTokenId: String
    @State private var isLoading = false
    @State private var error: String?
    @State private var result: String?
    @StateObject private var controller: PinInputController

    init(sessionToken: String, panTokenId: String) {
        self.panTokenId = panTokenId
        _controller = StateObject(
            wrappedValue: PinInputController(sessionToken: sessionToken)
        )
    }

    var body: some View {
        VStack {
            PinInput(
                controller: controller,
                onLoadingChange: { isLoading = $0 },
                onError: { error = $0 },
                onSuccess: { ebtResult in
                    switch onEnum(of: ebtResult) {
                    case .balance(let b):
                        result = "SNAP: \(b.balances.snapAvailableBalance ?? 0)"
                    case .payment:
                        result = "Payment successful"
                    }
                }
            )

            // Balance check
            Button("Check Balance") {
                controller.submit(
                    panTokenId: panTokenId,
                    operation: EbtOperation.BalanceCheck()
                )
            }
            .disabled(isLoading)

            // Payment
            Button("Pay") {
                controller.submit(
                    panTokenId: panTokenId,
                    operation: EbtOperation.Payment(paymentIntentId: "pi_xxx")
                )
            }
            .disabled(isLoading)
        }
    }
}

How it works

  1. Create a PinInputController as a @StateObject, passing the session token.
  2. Place PinInput in your view. It renders a secure numeric field for the PIN.
  3. Call controller.submit() with the PAN token ID (from card tokenization) and an EbtOperation.
  4. On success, onSuccess receives an EbtResult with the account balances.

EBT operations

OperationUsage
EbtOperation.BalanceCheck()Check the cardholder’s EBT balance.
EbtOperation.Payment(paymentIntentId:)Execute an EBT payment against a payment intent created on your backend.

EBT result

Use onEnum(of:) to pattern-match on the result:
switch onEnum(of: ebtResult) {
case .balance(let b):
    b.balances.snapAvailableBalance      // KotlinInt?
    b.balances.ebtCashAvailableBalance   // KotlinInt?
    b.balances.snapBeginningBalance      // KotlinInt?
    b.balances.ebtCashBeginningBalance   // KotlinInt?
case .payment(let p):
    p.balance.snapAvailableBalance       // KotlinInt?
    p.balance.ebtCashAvailableBalance    // KotlinInt?
}
The SDK returns balance amounts as integers in cents. Divide by 100 to display dollar amounts.
The onEnum(of:) helper enables exhaustive switch statements on Kotlin sealed classes in Swift.