Skip to main content

iOS API Reference

SDK Version: 1.0.0
Platform: iOS

Use every @MainActor type, property, and method on MainActor. Default values below match the formal signatures.

XTalkBleClient

@MainActor public final class XTalkBleClient: NSObject, ObservableObject
public override init()

Each client owns one BLE lifecycle and one serialized AT/RF channel. It cannot be reused after teardown().

Published state

PropertyTypeMeaning
connectionStateXTalkConnectionStateCurrent scan/connection state; read-only and observable as $connectionState
scanResults[XTalkScanResult]Devices in the current scan session
connectedNameString?Selected/connected device name
connectedAddressString?Address when the platform supplies it
connectedPeripheralIdUUID?Current peripheral identifier
atReadyBoolPassthrough write and notification readiness
deviceControlReadyBoolDevice-control query readiness
atEncodingXTalkAtEncodingActive AT encoding; defaults to .xor
eventsAsyncStream<XTalkBleEvent>Bounded event stream, finished by teardown()
onEvent(@MainActor (XTalkBleEvent) -> Void)?Optional single event callback

Connection and lifecycle methods

MethodPreconditionResult/notes
startScan()Client not torn downClears and updates results; failures publish .failed
stopScan()NoneStops scanning; scanning state returns to .idle
connect(id:)Bluetooth available, id from current resultsStops scanning and connects asynchronously; state/events publish the outcome
disconnect()NoneCancels connection and transactions; client can scan again
teardown()NoneEnds scan, connection, transactions, and events; idempotent and terminal

AT and RF methods

public func setAtEncoding(_ encoding: XTalkAtEncoding)
public func runAtCommand(
_ plain: String,
timeoutMs: Int = 3_000,
log: Bool = true,
responseMode: XTalkAtResponseMode = .automatic
) async throws -> [String]
public func sendAtCommandNoWait(_ plain: String) throws
public func withExclusiveAtSession<T>(
_ operation: @MainActor (XTalkBleClient) async throws -> T
) async throws -> T
public func sendRfPayload(_ rawPayload: Data, timeoutMs: Int = 5_000) async throws
public func sendRfPayloadNoWait(_ rawPayload: Data) throws
MethodInputReturn/error
setAtEncoding.plain or .xorResets the text decode buffer; never switch during a transaction
runAtCommandPlain AT text, positive timeout, response modeReturns response lines; can throw connection, readiness, busy, timeout, write, or cancellation errors
sendAtCommandNoWaitAT textStarts a write only; responses use events/observers and isolation can throw .busy
withExclusiveAtSessionSerialized async closureReturns closure result and prevents unrelated AT insertion
sendRfPayloadRaw business payloadWaits for complete send; throws on timeout or link failure
sendRfPayloadNoWaitRaw business payloadStarts send only; completion uses events and isolation can throw .busy

Device information queries

public func fetchBatteryLevel(timeoutMs: Int = 2_000) async -> Int?
public func fetchBleFirmwareVersion(timeoutMs: Int = 2_000) async -> String?
public func fetchHardwareVersion(timeoutMs: Int = 2_000) async -> String?

These convenience queries do not throw and return nil on failure. Check deviceControlReady first. Battery is the integer returned by the device; the SDK does not impose the customer's UI range or format. Version strings are trimmed.

Fine-grained observers

public func observeAtLines(_ handler: @escaping @MainActor (String) -> Void) -> UUID
public func removeAtLineObserver(_ id: UUID)
public func observeSendFinishLines(_ handler: @escaping @MainActor (String) -> Void) -> UUID
public func removeSendFinishLineObserver(_ id: UUID)
public func observePassthroughRaw(_ handler: @escaping @MainActor (Data) -> Void) -> UUID
public func removePassthroughRawObserver(_ id: UUID)

Registration returns a token accepted only by the matching remove method. Handlers run on MainActor; do not perform long blocking work in them.

Scan and connection models

XTalkScanResult

public init(id: UUID, name: String, address: String?, rssi: Int)
public var displayName: String { get }

displayName combines name and address when an address exists; otherwise it returns the name. Pass id to connect(id:).

XTalkConnectionState

.idle, .scanning, .connecting, .connected, .disconnected, .failed(XTalkBleError).

XTalkBleError

.bluetoothUnavailable, .permissionDenied, .scanFailed(String), .deviceNotFound(UUID), .deviceMismatch(String), .connectionFailed(String), .serviceMissing, .characteristicMissing, .notConnected, .notReady, .busy, .timeout, .writeFailed(String), .cancelled, .malformedResponse(String), .deviceControlFailed(String).

See Errors and FAQ for recovery guidance.

AT, RF, and event models

  • XTalkAtEncoding: .plain, .xor.
  • XTalkAtResponseMode: .automatic completes by command policy; .collectUntilTimeout collects the full timeout window.
  • XTalkDiMeta: optional diCnt, len, slot, snr, rssi, localSnr, localRssi, remoteSnr, remoteRssi, and txp; every initializer argument defaults to nil.
  • XTalkBleEvent.connectionStateChanged(XTalkConnectionState): state transition.
  • XTalkBleEvent.atLine(String): parsed AT line.
  • XTalkBleEvent.sendFinished: send completion.
  • XTalkBleEvent.diPayload(Data, XTalkDiMeta): business payload and radio metadata.
  • XTalkBleEvent.crcError(XTalkDiMeta, String): CRC metadata and raw line.
  • XTalkBleEvent.passthroughRaw(Data): unclassified raw passthrough bytes.

DeviceAuthTransport

@MainActor public protocol DeviceAuthTransport: AnyObject {
var label: String { get }
var atEncoding: DeviceAuthAtEncoding { get }
func setAtEncoding(_ encoding: DeviceAuthAtEncoding)
func runAtCommand(
_ command: String,
timeoutMs: Int,
log: Bool,
responseMode: DeviceAuthResponseMode
) async throws -> [String]
}

DeviceAuthAtEncoding has .plain and .xor. DeviceAuthResponseMode has .automatic, .collectFullWindow, and .recoverableEncodingProbe. The final two preserve full-window semantics. .recoverableEncodingProbe additionally requires a clean channel before the next command after ambiguous probing.

See the Device Authentication SDK for a complete BLE adapter.

XTalkDeviceAuthenticator

@MainActor public final class XTalkDeviceAuthenticator: ObservableObject {
@Published public private(set) var state: XTalkDeviceAuthState
public init(transport: any DeviceAuthTransport)
public func authenticate(
configuration: XTalkDeviceAuthConfiguration = XTalkDeviceAuthConfiguration()
) async throws -> XTalkDeviceAuthResult
public func cancel()
}

authenticate probes encoding, performs Challenge when required, reads identity, and returns the result. One instance allows one active session. Failure publishes .failed(error) and throws the same public error. cancel() requests cancellation and does nothing without an active session.

Authentication configuration, result, and state

XTalkDeviceAuthConfiguration

public init(
probeTimeoutMs: Int = 800,
strictProbeTimeoutMs: Int = 1_200,
challengeTimeoutMs: Int = 4_000,
identityTimeoutMs: Int = 4_000,
maxChallengeAttempts: Int = 3
)

Every value must be greater than zero or authentication throws .invalidConfiguration.

XTalkDeviceAuthResult

Contains identity: XTalkDeviceIdentity, encoding: DeviceAuthAtEncoding, and didRequireChallenge: Bool; its public initializer accepts the same fields.

XTalkDeviceAuthState

.idle, .probing, .challenging(attempt:maxAttempts:), .readingIdentity, .authenticated(XTalkDeviceIdentity), .failed(XTalkDeviceAuthError), .cancelled.

XTalkDeviceAuthError

.busy, .invalidConfiguration, .transportUnavailable, .commandTimedOut(String), .secureRandomUnavailable, .invalidChallenge, .challengeMissing, .malformedChallengeReply, .invalidSignatureLength(Int), .signatureVerificationFailed, .challengeRetryExhausted, .invalidDeviceIdentity, .cancelled.

Device identity and DID utilities

XTalkDeviceIdentity

public init(
deviceID: UInt32,
did: String,
serial: String,
source: XTalkDeviceIdentitySource
)

source is .efuseSn or .sn. Normal authentication consumers should use the returned identity instead of constructing one.

XTalkDeviceIDCodec

MethodPurpose
canonicalize(_ raw: UInt32) -> UInt32Normalize legacy device identifier encoding
fromSn(year: Int, week: Int, serial: UInt32) -> UInt32Build an identifier from SN fields using protocol bit widths
parse(_ raw: String) -> UInt32?Parse decimal or 0x hexadecimal text and canonicalize it
format(_ raw: UInt32) -> StringProduce canonical 0x plus eight uppercase hexadecimal digits

See the API coverage matrix