Requirements
- iOS 16 or higher
- Swift 5.9+
- SwiftUI
Installation
Add the Benny SDK as a Swift Package Manager dependency in Xcode:
- Go to File > Add Package Dependencies.
- Enter the repository URL:
https://github.com/Benny-API/benny-mobile-sdk
- Select the
BennySDK library product.
// 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()
}
}
}
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
- Create a
CardNumberInputController as a @StateObject, passing the session token from your backend.
- Place
CardNumberInput in your view. It renders a secure text field for the card number.
- Call
controller.tokenize() when the user is ready to submit.
- On success,
onSuccess receives a PAN token ID (string) that you can send to your backend.
Callbacks
| Callback | Type | Description |
|---|
onLoadingChange | (Bool) -> Void | Called with true when a request starts and false when it completes. |
onError | (String?) -> Void | Called with an error message on failure, or nil to clear a previous error. |
onSuccess | (String) -> Void | Called 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
- Create a
PinInputController as a @StateObject, passing the session token.
- Place
PinInput in your view. It renders a secure numeric field for the PIN.
- Call
controller.submit() with the PAN token ID (from card tokenization) and an EbtOperation.
- On success,
onSuccess receives an EbtResult with the account balances.
EBT operations
| Operation | Usage |
|---|
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.