Skip to content

iOS SBeacon Framework Data Models

AigarsM edited this page Feb 2, 2026 · 1 revision

Data Models

iOS SBeacon Framework v1.0.8

This guide documents all data models and type definitions in the iOS SBeacon Framework.


Beacon Models

SCPBeacon (CoreData Entity)

Paired beacon stored in CoreData:

public class SCPBeacon: NSManagedObject {
    // Identifiers
    @NSManaged public var uuid: UUID?
    @NSManaged public var major: NSNumber   // e.g., 1
    @NSManaged public var minor: NSNumber   // e.g., 26268
    @NSManaged public var macAddress: String?  // Full MAC (e.g., "A1:B2:C3:D4:E5:F6")
    @NSManaged public var address: UUID?    // BLE peripheral UUID

    // Status
    @NSManaged public var isInRange: Bool   // Currently connected
    @NSManaged public var range: NSNumber?  // RSSI signal strength
    @NSManaged public var lastSeenDateInternalUsageOnly: Date?

    // Configuration
    @NSManaged public var isCalibrated: Bool
    @NSManaged public var isCalibratedOn: Date?
    @NSManaged public var batteryLevel: NSNumber?  // 0-100%
    @NSManaged public var firmwareRevision: String?

    // Beacon timing
    @NSManaged public var timestamp: NSNumber?  // Beacon start time (milliseconds)

    // Relationships
    @NSManaged public var activities: NSSet?
    @NSManaged public var location: CLLocation?
}

Properties:

  • macAddress: Full 48-bit MAC address (6 octets)
  • isInRange: true when BLE connected
  • range: RSSI in dBm (-100 to 0, closer = higher)
  • timestamp: Beacon's power-on time in milliseconds (for converting beacon timestamps to real time)

UnpairedSBeacon

Unpaired beacon discovered during scanning:

public class UnpairedSBeacon: NSObject {
    public var minor: NSNumber              // iBeacon minor ID
    public var major: NSNumber              // iBeacon major ID
    public var macAddressEnding: String     // Last 4 bytes (e.g., "a3b5c7d9")
    public var peripheral: CBPeripheral?    // BLE peripheral
    public var lastTimeUpdated: Date        // Last seen time
    public let uuid: UUID                   // E2C56DB5-DFFB-48D2-B060-D0F5A71096E0
    public var range: NSNumber?             // RSSI signal strength
}

Usage:

  • Discovered during startScan()
  • Stored in SBeaconManager.shared.unpairedSBeacons
  • Removed after 20 seconds if not seen

Trip Models

TripStatus Enum

public enum TripStatus: Int {
    case Idle = 0      // No trip
    case Start = 1     // Trip starting
    case Active = 2    // Trip ongoing
    case End = 3       // Trip ending
    case Timeout = 4   // Trip timed out
    case Unknown = 6   // Unknown state

    var description: String {
        // "IDLE", "START", "ACTIVE", "END", "TIMEOUT", "UNKNOWN"
    }
}

TripRecord Struct

Trip record retrieved from beacon (9 bytes):

public struct TripRecord: Codable, Equatable {
    // Raw data (beacon time)
    let index: UInt8             // Record index (0-99)
    let startTimestamp: UInt32   // Start (beacon seconds)
    let endTimestamp: UInt32     // End (beacon seconds)

    // Real-world time (milliseconds)
    var actualStartTimestamp: Int64?
    var actualEndTimestamp: Int64?

    // Computed properties
    var startDate: Date?          // Computed from actualStartTimestamp
    var endDate: Date?            // Computed from actualEndTimestamp
    var duration: UInt32          // endTimestamp - startTimestamp (seconds)
    var durationInterval: TimeInterval  // Duration as TimeInterval
}

Binary Format:

[INDEX:1][START_TIMESTAMP:4][END_TIMESTAMP:4]

Timestamp Conversion:

// Beacon stores uptime in seconds
// actualTimestamp = beaconStartTime + (beaconTimestamp * 1000)
let actualStartMs = beaconTimestampOffset + Int64(startTimestamp) * 1000
let startDate = Date(timeIntervalSince1970: Double(actualStartMs) / 1000.0)

Driving Behavior Models

DrivingBehaviorEventType Enum

public enum DrivingBehaviorEventType: UInt8 {
    case acceleration = 4  // Harsh acceleration
    case braking = 5       // Harsh braking
    case cornering = 6     // Harsh cornering
    case accident = 8      // Collision/impact

    var description: String {
        // "Harsh Acceleration", "Harsh Braking", "Harsh Cornering", "Accident"
    }
}

DrivingBehaviorEvent Struct

Driving behavior event retrieved from beacon (18 bytes):

public struct DrivingBehaviorEvent: Codable, Equatable {
    // Identification
    let index: UInt8                     // Record index (0-79)
    let eventType: DrivingBehaviorEventType

    // Timing
    let timestamp: UInt32                // Beacon time (seconds)
    let duration: UInt16                 // Event duration (milliseconds)
    var actualTimestamp: Int64?          // Real time (milliseconds)
    var actualDate: Date?                // Computed date
    var durationSeconds: TimeInterval    // Duration in seconds

    // G-force measurements
    let avgMagnitude: Float              // Average G-force
    let maxMagnitude: Float              // Maximum G-force
    let avgX: Float                      // Average X-axis (G)
    let avgY: Float                      // Average Y-axis (G)
    let avgZ: Float                      // Average Z-axis (G)
}

Binary Format:

[INDEX:1][TYPE:1][TIMESTAMP:4][DURATION:2]
[AVG_MAG:2][MAX_MAG:2][AVG_X:2][AVG_Y:2][AVG_Z:2]

Units:

  • Raw values are in milli-G (1000 mG = 1 G)
  • Framework converts to G-force (Float): value / 1000.0

Example:

event.maxMagnitude  // 0.42 (G)
event.avgX          // -0.15 (G, left)
event.avgY          // 0.38 (G, forward)
event.avgZ          // 0.05 (G, up)

RealTimeDrivingBehaviorEvent Struct

Real-time event notification (4 bytes):

public struct RealTimeDrivingBehaviorEvent: Codable {
    let eventType: DrivingBehaviorEventType
    let axis: DrivingBehaviorAxis?       // x, y, z, or radial
    let axisRaw: UInt8                   // Raw axis byte
    let stateFlags: DrivingBehaviorStateFlags
    let receivedAt: Date                 // Local device time
}

Binary Format:

[0xAA][EVENT_TYPE][AXIS][STATE_FLAGS]

Marker Byte: 0xAA identifies real-time events


DrivingBehaviorAxis Enum

public enum DrivingBehaviorAxis: UInt8 {
    case x = 0x58       // Lateral (ASCII 'X')
    case y = 0x59       // Longitudinal (ASCII 'Y')
    case z = 0x5A       // Vertical (ASCII 'Z')
    case radial = 0x52  // Combined XY (ASCII 'R')

    var description: String {
        // "Lateral (X)", "Longitudinal (Y)", "Vertical (Z)", "Radial"
    }
}

DrivingBehaviorStateFlags Struct

public struct DrivingBehaviorStateFlags: OptionSet {
    public static let accidentX = DrivingBehaviorStateFlags(rawValue: 1 << 0)
    public static let accidentY = DrivingBehaviorStateFlags(rawValue: 1 << 1)
    public static let accidentZ = DrivingBehaviorStateFlags(rawValue: 1 << 2)
    public static let normalBraking = DrivingBehaviorStateFlags(rawValue: 1 << 3)
    public static let normalAcceleration = DrivingBehaviorStateFlags(rawValue: 1 << 4)
    public static let normalCornering = DrivingBehaviorStateFlags(rawValue: 1 << 5)
    public static let harshBraking = DrivingBehaviorStateFlags(rawValue: 1 << 6)
    public static let harshAcceleration = DrivingBehaviorStateFlags(rawValue: 1 << 7)
}

Usage:

if event.stateFlags.contains(.harshBraking) {
    print("Harsh braking detected")
}

DrivingBehaviorEnableFlags Struct

Configuration for enabling/disabling detection:

public struct DrivingBehaviorEnableFlags: Codable {
    let enabled: Bool                    // DB detection enabled
    let highPassFilterEnabled: Bool      // High-pass filter
    let lowPassFilterEnabled: Bool       // Low-pass filter

    var fdsValue: UInt16  // Convert to FDS storage value
}

DrivingBehaviorConfig Struct

Complete driving behavior configuration:

public struct DrivingBehaviorConfig {
    let corneringThreshold: UInt16       // mG (50-5000)
    let brakingThreshold: UInt16         // mG (50-5000)
    let accelerationThreshold: UInt16    // mG (50-5000)
    let durationThreshold: UInt16        // ms (1-10000)
    let enableFlagsValue: UInt16         // Enable bits

    // Constants
    public static let enableAllValue: UInt16
    public static let enableDBBit: UInt16 = 0x0001
    public static let enableHPBit: UInt16 = 0x0002
    public static let enableLPBit: UInt16 = 0x0004
}

Incident Models

IncidentRecord Class

Incident with accelerometer samples:

public class IncidentRecord: Codable {
    var id: Int                          // Incident ID
    var timestamp: Int64                 // Beacon time (seconds)
    var actualIncidentTimestamp: Int64   // Real time (milliseconds)
    var samples: Int                     // Number of vector samples
    var vectorValues: [IncidentVector]   // Accelerometer samples
    var averageVector: IncidentVector    // Average across samples
    var maximumResultantVector: Int      // Max magnitude (mG)
    var averageResultantVector: Int      // Average magnitude (mG)

    var actualDate: Date                 // Computed from actualIncidentTimestamp
}

IncidentVector Struct

public struct IncidentVector: Codable {
    var x: Int  // X-axis (milli-G)
    var y: Int  // Y-axis (milli-G)
    var z: Int  // Z-axis (milli-G)
}

Error Types

CalibrationError Enum

public enum CalibrationError: Error {
    case noBeacon  // No connected beacon or peripheral missing
}

Usage Examples

Working with TripRecord

let record: TripRecord = ...

// Check if timestamps are valid
guard let startDate = record.startDate,
      let endDate = record.endDate else {
    print("Invalid timestamps")
    return
}

// Format duration
let minutes = Int(record.duration / 60)
let hours = minutes / 60
let remainingMinutes = minutes % 60

print("Trip duration: \(hours)h \(remainingMinutes)m")
print("Started: \(startDate.formatted())")
print("Ended: \(endDate.formatted())")

Working with DrivingBehaviorEvent

let event: DrivingBehaviorEvent = ...

// Convert to G-force
print("Max G-force: \(String(format: "%.2f", event.maxMagnitude))G")

// Check axis dominance
if abs(event.avgX) > abs(event.avgY) {
    print("Lateral event (cornering)")
} else {
    print("Longitudinal event (acceleration/braking)")
}

// Duration in seconds
print("Duration: \(String(format: "%.1f", event.durationSeconds))s")

Working with RealTimeDrivingBehaviorEvent

let realtimeEvent: RealTimeDrivingBehaviorEvent = ...

print("Event type: \(realtimeEvent.eventType.description)")

if let axis = realtimeEvent.axis {
    print("Axis: \(axis.description)")
}

if realtimeEvent.stateFlags.contains(.harshBraking) {
    print("Harsh braking in progress")
}

Type Conformances

Identifiable

extension SCPBeacon: Identifiable {
    public var id: String {
        return macAddress ?? "\(minor)"
    }
}

extension UnpairedSBeacon: Equatable {
    public static func == (lhs: UnpairedSBeacon, rhs: UnpairedSBeacon) -> Bool {
        return lhs.major == rhs.major && lhs.minor == rhs.minor
    }
}

CustomStringConvertible

All models implement CustomStringConvertible for debugging:

print(tripRecord)
// "TripRecord(index: 5, start: 2026-01-30 10:00:00, end: 2026-01-30 10:45:00, duration: 2700s)"

print(drivingBehaviorEvent)
// "DrivingBehaviorEvent(index: 12, type: Harsh Braking, date: 2026-01-30 10:15:30, duration: 450ms, maxG: 0.42)"

Related Pages

Clone this wiki locally