Models and State
BagIdConfig
Configuration provided to BagIdSdk.configure().
-
Kotlin
-
Swift
data class BagIdConfig(
val apiBaseUrl: String,
val sourceAppKey: String,
val tokenProvider: suspend () -> String,
val theme: BagIdTheme? = null,
val localization: BagIdLocalization? = null,
)
// The Kotlin `suspend () -> String` is exposed as an `async throws` closure.
let config = BagIdConfig(
apiBaseUrl: "https://api.bagid.com",
sourceAppKey: "your-issued-client-key",
tokenProvider: { try await AuthBridge.federatedToken() },
theme: nil,
localization: nil
)
| Field | Type | Description |
|---|---|---|
|
|
Base URL of the BagID API (e.g., |
|
|
Issued client key for request authorization and routing. |
|
|
Function that returns a federated identity token on demand. Called silently by the SDK whenever authentication or re-authentication is needed. |
|
|
Optional styling for future or product-specific SDK UI surfaces (reserved; core transfer/clear flows are headless today). |
|
|
Optional locale and string overrides (reserved alongside |
BagIdTheme
-
Kotlin
-
Swift
data class BagIdTheme(
val primaryColor: String? = null,
val backgroundColor: String? = null,
val textColor: String? = null,
val errorColor: String? = null,
val fontFamily: String? = null,
val borderRadius: Int? = null,
val inputStyle: InputStyle? = null,
val buttonStyle: ButtonStyle? = null,
)
enum class InputStyle { OUTLINED, FILLED, UNDERLINED }
enum class ButtonStyle { FILLED, OUTLINED, TEXT }
// Nullable Int in Kotlin is bridged as `KotlinInt?` in Swift.
let theme = BagIdTheme(
primaryColor: "#003366",
backgroundColor: "#FFFFFF",
textColor: "#111111",
errorColor: "#B00020",
fontFamily: "System",
borderRadius: 8,
inputStyle: .outlined,
buttonStyle: .filled
)
All fields are optional. Unset fields use SDK defaults. Colors are hex strings (e.g., "#003366").
BagIdLocalization
-
Kotlin
-
Swift
data class BagIdLocalization(
val locale: String? = null,
val overrides: Map<String, String>? = null,
)
let localization = BagIdLocalization(
locale: "nb-NO",
overrides: [
"verification.title": "Bekreft eier"
]
)
| Field | Type | Description |
|---|---|---|
|
|
BCP 47 language tag (e.g., |
|
|
Per-string overrides keyed by string ID. Supports interpolation variables like |
InitResult
-
Kotlin
-
Swift
sealed class InitResult {
data object Authenticated : InitResult()
data class Unavailable(val reason: UnavailableReason) : InitResult()
}
enum class UnavailableReason {
TOKEN_PROVIDER_FAILED,
FEDERATION_REJECTED,
NETWORK_ERROR,
}
// Exhaustive matching on the sealed class via SKIE's onEnum(of:).
switch onEnum(of: result) {
case .authenticated:
break // continue
case .unavailable(let unavailable):
switch unavailable.reason {
case .tokenProviderFailed: print("Token provider failed")
case .federationRejected: print("Federation rejected")
case .networkError: print("Network error")
}
}
LoadJourneyRequest
-
Kotlin
-
Swift
sealed class LoadJourneyRequest {
data class Bcbp(val scanData: String, val airline: String? = null) : LoadJourneyRequest()
data class AirlinePayload(
val passengers: List<AirlinePassenger>,
) : LoadJourneyRequest()
}
// Construct either variant through its nested type:
let bcbpRequest: LoadJourneyRequest = LoadJourneyRequestBcbp(scanData: "M1SNOW/JON...", airline: nil)
let airlineRequest: LoadJourneyRequest = LoadJourneyRequestAirlinePayload(
passengers: [airlinePassenger]
)
AirlinePassenger
-
Kotlin
-
Swift
data class AirlinePassenger(
val passenger: Passenger,
val pnr: String,
val surname: String,
val flights: List<FlightInfo>,
val baggage: List<AirlineBaggageInput>? = null,
)
let airlinePassenger = AirlinePassenger(
passenger: Passenger(surname: "Snow", givenName: "Jon", title: "Mr"),
pnr: "A13712",
surname: "Snow",
flights: [flightInfo],
baggage: nil
)
Journey
Returned by loadJourney, createBaggageTag, and createAirlineBaggage.
-
Kotlin
-
Swift
data class Journey(
val journeyId: Int,
val passengers: List<Passenger>,
val flights: List<FlightInfo>,
val baggage: List<BaggageRecord>,
)
// `Int32` in Swift because Kotlin `Int` bridges to 32-bit.
let journeyId: Int32 = journey.journeyId
let passengers: [Passenger] = journey.passengers
let flights: [FlightInfo] = journey.flights
let baggage: [BaggageRecord] = journey.baggage
Passenger
-
Kotlin
-
Swift
data class Passenger(
val surname: String? = null,
val givenName: String? = null,
val title: String? = null,
)
let passenger = Passenger(
surname: "Snow",
givenName: "Jon",
title: "Mr"
)
FlightInfo
-
Kotlin
-
Swift
data class FlightInfo(
val flightNumber: String,
val departureAirport: String,
val destinationAirport: String,
val departureAt: String?,
)
let flight = FlightInfo(
flightNumber: "WF459",
departureAirport: "BGO",
destinationAirport: "OSL",
departureAt: "2023-01-21T10:00:00+01:00"
)
BaggageRecord
-
Kotlin
-
Swift
data class BaggageRecord(
val baggageId: Int,
val baggageTagNumber: String?,
val airline: String?,
val destinationAirport: String?,
)
let id: Int32 = baggage.baggageId
let tag: String? = baggage.baggageTagNumber
let airline: String? = baggage.airline
let destination: String? = baggage.destinationAirport
CreateBaggageTagRequest
-
Kotlin
-
Swift
data class CreateBaggageTagRequest(
val journeyId: Int,
val passengerList: List<Passenger>,
)
let request = CreateBaggageTagRequest(
journeyId: journey.journeyId,
passengerList: [passenger]
)
CreateAirlineBaggageRequest
-
Kotlin
-
Swift
data class CreateAirlineBaggageRequest(
val journeyId: Int,
val baggageTagNumber: String,
val airline: String,
val destinationAirport: String,
)
let request = CreateAirlineBaggageRequest(
journeyId: journey.journeyId,
baggageTagNumber: "0701913714",
airline: "WF",
destinationAirport: "OSL"
)
BagIdScanEvent
Scan results from BagIdSdk.scanForEbtTags() / scanForEbtTags(deviceId). Cold flow: collection starts the scan; cancelling stops it.
-
Kotlin
-
Swift
sealed class BagIdScanEvent : ScanEvent {
data class DeviceFound(val device: DiscoveredEbtBleDevice) : BagIdScanEvent(), ScanEvent.DeviceFound
data class ScanError(override val message: String) : BagIdScanEvent(), ScanEvent.ScanError
}
for await event in BagIdSdk.shared.scanForEbtTags() {
switch onEnum(of: event) {
case .deviceFound(let e):
print("Found: \(e.device.deviceId)")
case .scanError(let e):
print("Scan error: \(e.message)")
}
}
Only one scan collector should run at a time; starting another while the first is active may emit ScanError asking you to cancel the previous flow.
DiscoveredEbtBleDevice
One row from BLE discovery (after the SDK classifies the advertisement).
-
Kotlin
-
Swift
data class DiscoveredEbtBleDevice(
val identity: EbtTagIdentity,
val deviceId: String,
val signalStrength: Int,
)
let id: String = device.deviceId
let rssi: Int32 = device.signalStrength
let identity: EbtTagIdentity = device.identity
-
deviceId— Android: address-style id from the stack; iOS: CoreBluetooth peripheral identifier (string). -
signalStrength— RSSI in dBm. -
identity— vendor / hardware class the SDK inferred (see EbtTagIdentity).
EbtTagIdentity
Vendor and hardware class for an EBT. The SDK uses this to route to the correct internal BLE implementation; hosts normally only display it or pass discovery context via notifyDiscoveredModel.
-
Kotlin
sealed class EbtTagIdentity {
data class BagId(val model: BagIdDeviceType) : EbtTagIdentity()
data class BagIdBleV21(val model: BagIdDeviceType) : EbtTagIdentity()
data class OtherVendor(val modelHint: String?) : EbtTagIdentity()
}
BagIdDeviceType
BagID hardware line (from advertising / identity):
-
Kotlin
enum class BagIdDeviceType {
BAGID_GO,
BAGID_2FM,
BAGID_2ST,
}
ConnectedEbtDevice
Full BLE connection snapshot (see BagIdSdk.ebtBleDeviceState).
-
Kotlin
data class ConnectedEbtDevice(
val uuid: String,
val deviceId: String,
val identity: EbtTagIdentity,
val batteryLevel: Int?,
val firmwareVersion: FirmwareVersion?,
val serialNumber: String?,
)
Use uuid as TransferTagRequest.uniqueDeviceId / ClearTagRequest.uniqueDeviceId when it matches your backend device id (see transfer docs).
FirmwareVersion
BLE-reported firmware (com.bagid.sdk.ble.bagidEbt.domain.FirmwareVersion):
data class FirmwareVersion(val major: Int, val minor: Int, val revision: Int)
// toString() -> "major.minor.revision"
EbtBleDeviceState
-
Kotlin
data class EbtBleDeviceState(
val connectedDevice: ConnectedEbtDevice?,
val lastError: String?,
)
Emitted by BagIdSdk.ebtBleDeviceState; BLE errors are also mirrored into BagIdState.bleLastError.
TransferTagRequest
-
Kotlin
-
Swift
data class TransferTagRequest(
val deviceId: String,
val uniqueDeviceId: String,
val baggageId: Int,
val journeyId: Int,
val recordLocator: String? = null,
val custodyProofSurname: String? = null,
val displayTicket: EbtTicket? = null,
val journey: Journey? = null,
)
let request = TransferTagRequest(
deviceId: selectedDevice.deviceId,
uniqueDeviceId: connected.uuid,
baggageId: baggage.baggageId,
journeyId: journey.journeyId,
recordLocator: nil,
custodyProofSurname: nil,
displayTicket: nil,
journey: journey
)
Provide either displayTicket or a journey that contains baggageId, so the SDK can build the BLE ticket. When the lock lookup requires custody proof, supply recordLocator and custodyProofSurname (uppercase surname as required by the API), or handle CustodyProofRequiredException and retry.
TransferResult
-
Kotlin
-
Swift
data class TransferResult(
val bleWriteSuccess: Boolean,
val attachmentSuccess: Boolean,
val pendingRetry: Boolean,
val device: DeviceInfo,
)
let wrote: Bool = transfer.bleWriteSuccess
let attached: Bool = transfer.attachmentSuccess
let pending: Bool = transfer.pendingRetry
let device: DeviceInfo = transfer.device
pendingRetry is true when the BLE write succeeded but device attachment HTTP failed. The host can surface this for retry or support follow-up; there is no separate attachment queue in the SDK beyond this flag.
ClearTagRequest
-
Kotlin
-
Swift
data class ClearTagRequest(
val deviceId: String,
val uniqueDeviceId: String,
)
let request = ClearTagRequest(
deviceId: selectedDevice.deviceId,
uniqueDeviceId: connected.uuid
)
ClearResult
-
Kotlin
-
Swift
data class ClearResult(
val success: Boolean,
val unlocked: Boolean,
)
let success: Bool = result.success
let unlocked: Bool = result.unlocked
DeviceInfo
-
Kotlin
-
Swift
data class DeviceInfo(
val deviceId: String,
val serialNumber: String?,
val batteryLevel: Int?,
val firmwareVersion: String?,
)
let deviceId: String = info.deviceId
let serial: String? = info.serialNumber
let battery: Int? = info.batteryLevel.map { Int(truncating: $0) } // KotlinInt? → Int?
let firmware: String? = info.firmwareVersion
BagIdState
Observable state emitted by BagIdSdk.state.
-
Kotlin
-
Swift
data class BagIdState(
val authenticated: Boolean,
val sessionExpired: Boolean,
val connectedDevice: DeviceInfo?,
val lastError: String?,
val bleLastError: String?,
val identifiedEbtTagIdentity: EbtTagIdentity? = null,
)
// `StateFlow<BagIdState>` is bridged to `AsyncSequence` by SKIE.
for await state in BagIdSdk.shared.state {
let authenticated: Bool = state.authenticated
let sessionExpired: Bool = state.sessionExpired
let device: DeviceInfo? = state.connectedDevice
let lastError: String? = state.lastError
let bleLastError: String? = state.bleLastError
let tagIdentity: EbtTagIdentity? = state.identifiedEbtTagIdentity
}
-
lastError— HTTP / orchestration failures from façade APIs. -
bleLastError— last error fromebtBleDeviceState. -
identifiedEbtTagIdentity— fromnotifyDiscoveredModeland refreshed after connect.
EbtType and vendor BLE extras
EbtType selects vendor-specific optional BLE features that are not on the core façade (nametag is one example today for EbtType.BagId via EbtBleExtras). It does not choose which physical tag you have—that comes from BLE discovery (EbtTagIdentity).
sealed class EbtType {
data object BagId : EbtType()
}
interface EbtBleExtras {
suspend fun sendNametag(header: String, imageBytes: ByteArray): Result<Unit>
}