AnyBio
API Reference

Events

Event types — BioEvent, lifecycle events, session events, connection states, and stream states.

The SDK communicates state changes through a Combine-based event system. Subscribe to sdk.events to receive all events.

sdk.events.sink { event in
    switch event {
    case .lifecycle(let e): handleLifecycle(e)
    case .sample(let deviceId, let sample): handleSample(deviceId, sample)
    case .deviceState(let device, let conn, let stream): handleState(device, conn, stream)
    case .session(let e): handleSession(e)
    case .notification(let e): handleNotification(e)
    }
}

BioEvent

Top-level event enum emitted by BioSDKClient.events.

public enum BioEvent: Equatable {
    case lifecycle(BioLifecycleEvent)
    case sample(deviceId: String, BioSample)
    case deviceState(device: BioDevice, connection: BioConnectionState, stream: BioStreamState)
    case session(BioSessionEvent)
    case notification(BioNotificationEvent)
}
CaseDescription
.lifecycle(...)Device discovered, connected, or disconnected
.sample(deviceId:, ...)Biosignal sample received from a device
.deviceState(...)Connection or streaming state changed
.session(...)Backend session lifecycle event
.notification(...)Push notification event

BioLifecycleEvent

Device discovery and connection lifecycle events.

public enum BioLifecycleEvent: Equatable {
    case discovered(BioDevice)
    case connected(BioDevice)
    case disconnected(BioDevice, errorMessage: String?, errorCode: Int?, errorDomain: String?)
}
CaseDescription
.discovered(device)A new device was found during scanning
.connected(device)Device successfully connected
.disconnected(device, ...)Device disconnected (with optional error details)

Example:

sdk.events
    .compactMap { event -> BioDevice? in
        guard case .lifecycle(.connected(let device)) = event else { return nil }
        return device
    }
    .sink { device in
        print("Connected to \(device.name)")
    }

BioSessionEvent

Backend session lifecycle events.

public enum BioSessionEvent: Equatable {
    case started(id: String, devices: [SessionDevice])
    case resumed(id: String, devices: [SessionDevice])
    case conflict(activeId: String, startedAt: Date?)
    case ended(id: String, ok: Bool)
    case endFailed(id: String, status: Int?)
    case recoveryAvailable(info: OrphanedSessionInfo)
    case forbidden
    case serverError(status: Int)
    case error(message: String)
}
CaseDescription
.started(id:, devices:)New session created on the backend
.resumed(id:, devices:)Existing session resumed
.conflict(activeId:, startedAt:)HTTP 409 — another session is active
.ended(id:, ok:)Session ended (ok indicates clean shutdown)
.endFailed(id:, status:)Session end request failed
.recoveryAvailable(info:)An orphaned session from a previous launch was detected
.forbiddenAuthentication/authorization failure (HTTP 403)
.serverError(status:)Backend returned a server error
.error(message:)General session error

SessionDevice

Device info returned when a session starts or resumes.

public struct SessionDevice: Equatable, Codable {
    public let ingest_device_id: String
    public let device_id: String
    public let device_profile_id: String?
    public let make: String?
    public let model: String?
}

OrphanedSessionInfo

Information about a session that was left active from a previous app launch.

public struct OrphanedSessionInfo: Equatable, Sendable {
    public let sessionId: String
    public let startedAt: Date
    public let deviceCount: Int
    public let totalChunks: Int
}

BioNotificationEvent

Events from the notification system.

public enum BioNotificationEvent: Equatable {
    case received(BioNotification)
    case acknowledged(notificationId: String)
    case dismissed(notificationId: String)
    case connectionStateChanged(NotificationConnectionState)
}

See the Notifications API reference for BioNotification and NotificationConnectionState.


BioConnectionState

Connection state for a device.

public enum BioConnectionState: String, Equatable, Codable {
    case discovered
    case connecting
    case discoveringServices
    case ready
    case disconnected
    case failed
}

State Flow

discovered → connecting → discoveringServices → ready
                                               → failed
                                               → disconnected
StateDescription
.discoveredDevice found during scan, not yet connected
.connectingConnection in progress
.discoveringServicesConnected, discovering GATT services
.readyFully connected with services discovered
.disconnectedDevice disconnected
.failedConnection attempt failed

BioStreamState

Streaming state for a device.

public enum BioStreamState: String, Equatable, Codable {
    case idle
    case starting
    case streaming
    case stopping
    case stalled
    case unsupported
}

State Flow

idle → starting → streaming → stopping → idle
                → stalled
                → unsupported
StateDescription
.idleNot streaming
.startingStream start command sent, waiting for data
.streamingActively receiving biosignal data
.stoppingStream stop command sent
.stalledStream started but no data received (timeout)
.unsupportedDevice does not support streaming

Subscribing to Events

Filter by Event Type

// Only session events
let sessionEvents = sdk.events
    .compactMap { event -> BioSessionEvent? in
        guard case .session(let e) = event else { return nil }
        return e
    }

// Only samples from a specific device
let deviceSamples = sdk.events
    .compactMap { event -> BioSample? in
        guard case .sample(let id, let sample) = event,
              id == myDeviceId else { return nil }
        return sample
    }

Observe Device State Changes

sdk.events
    .compactMap { event -> (BioDevice, BioConnectionState, BioStreamState)? in
        guard case .deviceState(let device, let conn, let stream) = event
        else { return nil }
        return (device, conn, stream)
    }
    .sink { device, connection, stream in
        print("\(device.name): \(connection) / \(stream)")
    }

On this page