Skip to main content

Installation

Swift Package Manager

In Xcode: File → Add Packages, then enter:
https://github.com/steadpay/steadpay-swift
Or in Package.swift:
.package(url: "https://github.com/steadpay/steadpay-swift.git", from: "1.0.0")
Add SteadpayUI as a target dependency (and optionally Steadpay for direct client usage):
.target(
    name: "YourApp",
    dependencies: [
        .product(name: "SteadpayUI", package: "steadpay-swift"),
    ]
)

Quick start

Wrap the authenticated portion of your app in SteadpayGate:
import SteadpayUI

SteadpayGate(
    tenantSlug: "acme",
    customerId: currentUser.stripeCustomerId,
    publishableKey: "pk_live_abc123",
    apiBase: "https://api.steadpayhq.com"
) {
    YourApp()
}
  • ActiveYourApp renders normally
  • Warning — dismissable banner above content
  • Lockout — full-screen overlay until card is updated

Parameters

ParameterTypeRequiredDefault
tenantSlugString
customerIdString
publishableKeyString
apiBaseString
pollIntervalTimeInterval600
forcedStatusSteadpayStatus?nil
callbacksSteadpayCallbacks?nil
lockoutScreen((@escaping () -> Void, Entitlements?) -> AnyView)?built-in
warningBanner((@escaping () -> Void, @escaping () -> Void) -> AnyView)?built-in
content@ViewBuilder

Callbacks

SteadpayGate(
    tenantSlug: "acme",
    customerId: currentUser.stripeCustomerId,
    publishableKey: "pk_live_abc123",
    apiBase: "https://api.steadpayhq.com",
    callbacks: SteadpayCallbacks(
        onLockout: { print("locked out") },
        onWarning: { print("warning") },
        onActive: { print("active") },
        onRecovered: { print("recovered") },
        onError: { err in print("error: \(err)") }
    )
) {
    YourApp()
}
Callbacks fire on status transitions only.

Custom enforcement UI

Wrap your views in AnyView(...):
SteadpayGate(
    // ...
    lockoutScreen: { triggerCardUpdate, _ in
        AnyView(MyBrandedLockout(onUpdate: triggerCardUpdate))
    },
    warningBanner: { triggerCardUpdate, dismissWarning in
        AnyView(MyBrandedBanner(onUpdate: triggerCardUpdate, onDismiss: dismissWarning))
    }
) {
    YourApp()
}

Testing

Force a state

SteadpayGate(
    tenantSlug: "acme",
    customerId: "cus_test",
    publishableKey: "pk_test_abc",
    apiBase: "https://api.steadpayhq.com",
    forcedStatus: .lockout
) {
    YourApp()
}
No network calls are made when forcedStatus is set. Remove before shipping.

Interactive sandbox

import SteadpayUI

SteadpaySandbox(
    onLockout: { print("locked out") },
    onWarning: { print("warning") },
    onActive: { print("active") },
    onError: { err in print("error: \(err)") }
) {
    YourApp()
}
A DEV badge appears in the bottom-right corner. Tap to open a sheet with state pills and a callback log. Remove before shipping to production.

UIKit / non-SwiftUI usage

Use the Steadpay target directly with Combine:
import Steadpay

let client = SteadpayClient(
    config: SteadpayConfig(
        apiBase: "https://api.steadpayhq.com",
        tenantSlug: "acme",
        customerId: currentUser.stripeCustomerId,
        publishableKey: "pk_live_abc123"
    )
)
client.start()

client.$status
    .receive(on: DispatchQueue.main)
    .sink { status in /* update UI */ }
    .store(in: &cancellables)
Call client.stop() in deinit or viewDidDisappear.