Repository navigation
iOS SBeacon Framework Data Models
iOS SBeacon Framework v1.0.8
This guide documents all data models and type definitions in the iOS SBeacon Framework.
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:truewhen 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)
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
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"
}
}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)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"
}
}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)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
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"
}
}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")
}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
}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 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
}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)
}public enum CalibrationError: Error {
case noBeacon // No connected beacon or peripheral missing
}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())")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")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")
}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
}
}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)"- Beacon Scanning - UnpairedSBeacon usage
- Trip Management - TripRecord usage
- Driving Behavior - DrivingBehaviorEvent usage
- API Reference - Complete method signatures
Consolidated SDK
SmartDrive
PortalApiBase
PortalApiClient
Wellness
SBeacon
- Overview
- Installation
- Initialization
- Beacon Scanning
- Beacon Pairing
- Trip Management
- Driving Behavior
- Calibration
- Event Handling
- Data Models
- API Reference
Consolidated SPM
SDApiManager
CCFrameworkOpen
SmartDrive
SBeacon