iOS SBeacon Framework Data Models - scopetech/Mobile-Public GitHub Wiki
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:truewhen BLE connectedrange: 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
- Beacon Scanning - UnpairedSBeacon usage
- Trip Management - TripRecord usage
- Driving Behavior - DrivingBehaviorEvent usage
- API Reference - Complete method signatures