Summaries
DataModels Summaries iOS
Module for retrieving and storing data and calculating summaries.
Slot intervals
Slots in summaries have a 1 minute interval, or our default intervals. This can be set when initialising the SDK.
BPM / Heartrate summary
Data structure
public final class MetricBPMSummery : Object {
@objc public dynamic var id: Int = 0
@objc public dynamic var date: Date = Date()
@objc public dynamic var averageBPM: Int = 0
@objc public dynamic var lastBPM: Int = 0
@objc public dynamic var maxDayBPM: Int = 0
@objc public dynamic var durationRestZone: Int = 0
@objc public dynamic var durationWarmUpZone: Int = 0
@objc public dynamic var durationFatBurnZone: Int = 0
@objc public dynamic var durationEnduranceZone: Int = 0
@objc public dynamic var durationPerformanceZone: Int = 0
@objc public dynamic var durationMaxZone: Int = 0
@objc public dynamic var percentageRestZone: Int = 0
@objc public dynamic var percentageWarmUpZone: Int = 0
@objc public dynamic var percentageFatBurnZone: Int = 0
@objc public dynamic var percentageEnduranceZone: Int = 0
@objc public dynamic var percentagePerformanceZone: Int = 0
@objc public dynamic var percentageMaxZone: Int = 0
@objc public dynamic var readyForSync: Bool = false
let slots = List<MetricBPMSummerySlot>()
}
Notes
- Summaries are always for a full day from 00:00 till 29:59 local phone time.
- All Percent variables = 0-100
- All Duration variables = Int in minutes
- Range of all BPM values -> 30-220
BPM / Heartrate summary slots
Data structure
open class MetricBPMSummerySlot : Object {
@objc public dynamic var intervalDate: Date = Date()
@objc public dynamic var bpm: Int = 0
}
Example with real data
MetricBPMSummery {
id = 2;
date = 2022-02-01 00:00:00 +0000;
averageBPM = 64;
lastBPM = 69;
maxDayBPM = 89;
durationRestZone = 30000;
durationWarmUpZone = 300;
durationFatBurnZone = 0;
durationEnduranceZone = 0;
durationPerformanceZone = 0;
durationMaxZone = 0;
percentageRestZone = 99;
percentageWarmUpZone = 1;
percentageFatBurnZone = 0;
percentageEnduranceZone = 0;
percentagePerformanceZone = 0;
percentageMaxZone = 0;
readyForSync = 1;
slots = List<MetricBPMSummerySlot> <0x281881130> (
[0] MetricBPMSummerySlot {
intervalDate = 2022-02-01 00:00:00 +0000;
bpm = 89;
},
[1] MetricBPMSummerySlot {
intervalDate = 2022-02-01 00:05:00 +0000;
bpm = 79;
},
[2] MetricBPMSummerySlot {
intervalDate = 2022-02-01 00:10:00 +0000;
bpm = 81;
},
[3] MetricBPMSummerySlot {
intervalDate = 2022-02-01 00:15:00 +0000;
bpm = 75;
},
[4] MetricBPMSummerySlot {
intervalDate = 2022-02-01 00:20:00 +0000;
bpm = 69;
},
[5] MetricBPMSummerySlot {
intervalDate = 2022-02-01 00:25:00 +0000;
bpm = 65;
},
[6] MetricBPMSummerySlot {
intervalDate = 2022-02-01 00:30:00 +0000;
bpm = 60;
},
[7] MetricBPMSummerySlot {
intervalDate = 2022-02-01 00:35:00 +0000;
bpm = 57;
})
}
Breaths / Respirationrate summary
Data structure
public final class MetricBreathSummery : Object {
@objc public dynamic var id: Int = 0
@objc public dynamic var date: Date = Date()
@objc public dynamic var averageBreath: Int = 0
@objc public dynamic var lastBreath: Int = 0
@objc public dynamic var maxDayBreath: Int = 0
@objc public dynamic var readyForSync: Bool = false
let slots = List<MetricBreathSummerySlot>()
}
Notes
- Summaries are always for a full day from 00:00 till 29:59 local phone time.
- Range of all brpm values -> 5-45 brpm
Breaths / Respirationrate summary slots
Data structure
open class MetricBreathSummerySlot : Object {
@objc public dynamic var intervalDate: Date = Date()
@objc public dynamic var brpm: Int = 0
@objc public dynamic var brpmQ: Int = 0
}
Notes
- brpmQ -> Quality of the raw respiration rate. 4 means very good quality respiration rate, while 0 means bad quality
Example with real data
MetricBreathSummery {
id = 2;
date = 2022-02-01 00:00:00 +0000;
averageBreath = 57;
lastBreath = 48;
maxDayBreath = 63;
readyForSync = 1;
slots = List<MetricBreathSummerySlot> <0x282567390> (
[0] MetricBreathSummerySlot {
intervalDate = 2022-02-01 00:00:00 +0000;
brpm = 62;
brpmQ = 0;
},
[1] MetricBreathSummerySlot {
intervalDate = 2022-02-01 00:30:00 +0000;
brpm = 51;
brpmQ = 0;
},
[2] MetricBreathSummerySlot {
intervalDate = 2022-02-01 01:00:00 +0000;
brpm = 57;
brpmQ = 0;
},
[3] MetricBreathSummerySlot {
intervalDate = 2022-02-01 01:30:00 +0000;
brpm = 57;
brpmQ = 0;
},
[4] MetricBreathSummerySlot {
intervalDate = 2022-02-01 02:00:00 +0000;
brpm = 61;
brpmQ = 0;
},
[5] MetricBreathSummerySlot {
intervalDate = 2022-02-01 02:30:00 +0000;
brpm = 60;
brpmQ = 0;
},
[6] MetricBreathSummerySlot {
intervalDate = 2022-02-01 03:00:00 +0000;
brpm = 57;
brpmQ = 0;
},
[7] MetricBreathSummerySlot {
intervalDate = 2022-02-01 03:30:00 +0000;
brpm = 56;
brpmQ = 0;
},
[8] MetricBreathSummerySlot {
intervalDate = 2022-02-01 04:00:00 +0000;
brpm = 58;
brpmQ = 0;
},
[9] MetricBreathSummerySlot {
intervalDate = 2022-02-01 04:30:00 +0000;
brpm = 56;
brpmQ = 0;
},
[10] MetricBreathSummerySlot {
intervalDate = 2022-02-01 05:00:00 +0000;
brpm = 57;
brpmQ = 0;
})
}
Steps summary
Data structure
public final class MetricStepsSummary : Object {
@objc public dynamic var id: Int = 0
@objc public dynamic var date: Date = Date()
@objc public dynamic var stepCount: Int = 0
@objc public dynamic var stepDistance: Int = 0
@objc public dynamic var stepCalories: Double = 0
@objc public dynamic var goalStepPercentage: Int = 0
@objc public dynamic var moveEveryHour: Int = 0
@objc public dynamic var workoutsCount: Int = 0
}
Notes
- Summaries are always for a full day from 00:00 till 29:59 local phone time.
- StepCount -> 0-65535 steps
- StepDistance -> (steps userHeight 0.414) / 100
- StepCalories -> kcal (daily sum)
- GoalStepPercentage -> steps count / 10 000 distance in meters
- ActivityType -> UNSPECIFIED = 0, OTHER = 1, WALK = 2, RUN = 4, CYCLE = 6, REST = 7
Steps summary slots
Data structure
public final class MetricStepsSummarySlot : Object {
@objc public dynamic var intervalDate: Date = Date()
@objc public dynamic var stepCount: Int = 0
@objc public dynamic var energyExp: Double = 0
@objc public dynamic var speed: Double = 0
@objc public dynamic var activityType: Int = 0
@objc public dynamic var distance: Int = 0
}
Example with real data
MetricStepsSummary {
id = 2;
date = 2022-02-01 00:00:00 +0000;
stepCount = 37;
stepDistance = 25;
stepCalories = 569.658;
goalStepPercentage = 0;
moveEveryHour = 0;
workoutsCount = 0;
slots = List<MetricStepsSummarySlot> <0x280a41600> (
[0] MetricStepsSummarySlot {
intervalDate = 2022-02-01 00:00:00 +0000;
stepCount = 15;
energyExp = 39.16399999999998;
speed = 7;
activityType = 7;
distance = 10;
},
[1] MetricStepsSummarySlot {
intervalDate = 2022-02-01 00:30:00 +0000;
stepCount = 0;
energyExp = 33.645;
speed = 12;
activityType = 7;
distance = 0;
},
[2] MetricStepsSummarySlot {
intervalDate = 2022-02-01 01:00:00 +0000;
stepCount = 0;
energyExp = 33.042;
speed = 0;
activityType = 7;
distance = 0;
},
[3] MetricStepsSummarySlot {
intervalDate = 2022-02-01 01:30:00 +0000;
stepCount = 0;
energyExp = 33.06;
speed = 6;
activityType = 7;
distance = 0;
},
[4] MetricStepsSummarySlot {
intervalDate = 2022-02-01 02:00:00 +0000;
stepCount = 0;
energyExp = 33.06;
speed = 3;
activityType = 7;
distance = 0;
},
[5] MetricStepsSummarySlot {
intervalDate = 2022-02-01 02:30:00 +0000;
stepCount = 0;
energyExp = 34.32700000000001;
speed = 9;
activityType = 7;
distance = 0;
},
[6] MetricStepsSummarySlot {
intervalDate = 2022-02-01 03:00:00 +0000;
stepCount = 10;
energyExp = 33.69;
speed = 10;
activityType = 7;
distance = 7;
})
}
Activity summary
Data structure
public final class MetricActivitySummery : Object {
@objc public dynamic var id: Int = 0
@objc public dynamic var date: Date = Date()
@objc public dynamic var steps: Int = 0
@objc public dynamic var distance: Int = 0
@objc public dynamic var calories: Double = 0
@objc public dynamic var readyForSync: Bool = false
}
Notes
- Summaries are always for a full day from 00:00 till 29:59 local phone time.
- Steps -> 0-65535 steps
- Distance -> (steps userHeight 0.414) / 100
- Calories -> kcal (daily sum)
Activity summary slots
Data structure
public final class MetricActivitySummerySlot : Object {
@objc public dynamic var intervalDate: Date = Date()
@objc public dynamic var steps: Int = 0
@objc public dynamic var distance: Int = 0
@objc public dynamic var calories: Double = 0
@objc public dynamic var isEdited: Bool = false
}
Example with real data
MetricActivitySummery {
id = 1;
date = 2022-01-31 00:00:00 +0000;
steps = 84;
distance = 59;
calories = 183.1019999999999;
readyForSync = 0;
slots = List<MetricActivitySummerySlot> <0x281044bb0> (
[0] MetricActivitySummerySlot {
intervalDate = 2022-01-31 00:00:00 +0000;
steps = 0;
distance = 0;
calories = 0;
isEdited = 0;
},
[1] MetricActivitySummerySlot {
intervalDate = 2022-01-31 00:30:00 +0000;
steps = 0;
distance = 0;
calories = 0;
isEdited = 0;
},
[2] MetricActivitySummerySlot {
intervalDate = 2022-01-31 01:00:00 +0000;
steps = 0;
distance = 0;
calories = 0;
isEdited = 0;
})
}
Skin temperature summary (V1 + V2 bracelet)
Data structure
public final class MetricBodyTempSummery : Object {
@objc public dynamic var id: Int = 0
@objc public dynamic var date: Date = Date()
@objc public dynamic var averageTemp: Double = 0.0
@objc public dynamic var lastTemp: Double = 0.0
@objc public dynamic var maxDayTemp: Double = 0.0
@objc public dynamic var readyForSync: Bool = false
let slots = List<MetricBodyTempSummerySlot>()
}
Notes
- Summaries are always for a full day from 00:00 till 29:59 local phone time.
- Temperature -> Celsius
Skin temperature summary Slots (V1 + V2 bracelet)
Data structure
public final class MetricBodyTempSummerySlot : Object {
@objc public dynamic var intervalDate: Date = Date()
@objc public dynamic var temp: Double = 0.0
}
Example with real data
MetricBodyTempSummery {
id = 2;
date = 2022-02-01 00:00:00 +0000;
averageTemp = 34.5756973805147;
lastTemp = 35.2425;
maxDayTemp = 36.0487890625;
readyForSync = 1;
slots = List<MetricBodyTempSummerySlot> <0x283930790> (
[0] MetricBodyTempSummerySlot {
intervalDate = 2022-02-01 00:00:00 +0000;
temp = 31.254609375;
},
[1] MetricBodyTempSummerySlot {
intervalDate = 2022-02-01 00:10:00 +0000;
temp = 34.0364453125;
},
[2] MetricBodyTempSummerySlot {
intervalDate = 2022-02-01 00:20:00 +0000;
temp = 34.99583984375;
},
[3] MetricBodyTempSummerySlot {
intervalDate = 2022-02-01 00:30:00 +0000;
temp = 35.64037109375;
},
[4] MetricBodyTempSummerySlot {
intervalDate = 2022-02-01 00:40:00 +0000;
temp = 35.57447265625;
},
[5] MetricBodyTempSummerySlot {
intervalDate = 2022-02-01 00:50:00 +0000;
temp = 35.75228515625;
},
[6] MetricBodyTempSummerySlot {
intervalDate = 2022-02-01 01:00:00 +0000;
temp = 35.94892578125;
},
[7] MetricBodyTempSummerySlot {
intervalDate = 2022-02-01 01:10:00 +0000;
temp = 35.11261718750001;
},
[8] MetricBodyTempSummerySlot {
intervalDate = 2022-02-01 01:20:00 +0000;
temp = 35.65376953125;
},
[9] MetricBodyTempSummerySlot {
intervalDate = 2022-02-01 01:30:00 +0000;
temp = 35.81080078125;
})
}
Core body temperature summary (V2 bracelet ONLY)
Data structure
public final class MetricCoreBodyTemperatureSummary : Object {
@objc public dynamic var id: Int = 0
@objc public dynamic var date: Date = Date()
@objc public dynamic var averageCoreBodyTemperature: Double = 0.0
@objc public dynamic var lastCoreBodyTemperature: Double = 0.0
@objc public dynamic var maxDayCoreBodyTemperature: Double = 0.0
@objc public dynamic var readyForSync: Bool = false
let slots = List<MetricCoreBodyTemperatureSummerySlot>()
}
Notes
- Summaries are always for a full day from 00:00 till 29:59 local phone time.
- Temperature -> 15-45 °C
Core body temperature summary Slots (V2 bracelet ONLY)
Data structure
public final class MetricCoreBodyTemperatureSummerySlot : Object {
@objc public dynamic var intervalDate: Date = Date()
@objc public dynamic var coreBodyTemperature: Double = 0.0
}
Example with real data
MetricCoreBodyTemperatureSummary {
id = 2;
date = 2022-02-01 00:00:00 +0000;
averageCoreBodyTemperature = 34.80532191193937;
lastCoreBodyTemperature = 35.22504690647125;
maxDayCoreBodyTemperature = 35.75226019894704;
readyForSync = 1;
slots = List<MetricCoreBodyTemperatureSummerySlot> <0x2823f91e0> (
[0] MetricCoreBodyTemperatureSummerySlot {
intervalDate = 2022-02-01 00:00:00 +0000;
coreBodyTemperature = 37.99549363851547;
},
[1] MetricCoreBodyTemperatureSummerySlot {
intervalDate = 2022-02-01 00:30:00 +0000;
coreBodyTemperature = 37.75226019894704;
},
[2] MetricCoreBodyTemperatureSummerySlot {
intervalDate = 2022-02-01 01:00:00 +0000;
coreBodyTemperature = 37.65390893731266;
},
[3] MetricCoreBodyTemperatureSummerySlot {
intervalDate = 2022-02-01 01:30:00 +0000;
coreBodyTemperature = 36.5227193553932;
},
[4] MetricCoreBodyTemperatureSummerySlot {
intervalDate = 2022-02-01 02:00:00 +0000;
coreBodyTemperature = 37.40649285942316;
},
[5] MetricCoreBodyTemperatureSummerySlot {
intervalDate = 2022-02-01 02:30:00 +0000;
coreBodyTemperature = 36.43724005004391;
},
[6] MetricCoreBodyTemperatureSummerySlot {
intervalDate = 2022-02-01 03:00:00 +0000;
coreBodyTemperature = 36.66596518486739;
},
[7] MetricCoreBodyTemperatureSummerySlot {
intervalDate = 2022-02-01 03:30:00 +0000;
coreBodyTemperature = 36.58491187788546;
},
[8] MetricCoreBodyTemperatureSummerySlot {
intervalDate = 2022-02-01 04:00:00 +0000;
coreBodyTemperature = 36.6835565895401;
})
}
Saturation / Spo2 summary (V2 bracelet ONLY)
Data structure
public final class MetricSpo2Summery : Object {
@objc public dynamic var id: Int = 0
@objc public dynamic var date: Date = Date()
@objc public dynamic var averageSpo2: Int = 0
@objc public dynamic var lastSpo2: Int = 0
@objc public dynamic var maxDaySpo2: Int = 0
@objc public dynamic var readyForSync: Bool = false
let slots = List<MetricSpo2SummerySlot>()
}
Notes
- Summaries are always for a full day from 00:00 till 29:59 local phone time.
- Spo2 -> 0-100%
Saturation / Spo2 summary slots (V2 bracelet ONLY)
Data structure
public final class MetricSpo2SummerySlot : Object {
@objc public dynamic var intervalDate: Date = Date()
@objc public dynamic var spo2: Double = 0
}
Example with real data
MetricSpo2Summery {
id = 2;
date = 2022-02-01 00:00:00 +0000;
averageSpo2 = 98;
lastSpo2 = 97;
maxDaySpo2 = 99;
readyForSync = 1;
slots = List<MetricSpo2SummerySlot> <0x2823f91e0> (
[0] MetricSpo2SummerySlot {
intervalDate = 2022-02-01 00:00:00 +0000;
spo2 = 97;
})
}
EDA / Emography summary (V2 bracelet ONLY)
Data structure
public final class MetricEmographySummary : Object {
@objc public dynamic var id: Int = 0
@objc public dynamic var date: Date = Date()
@objc public dynamic var stressLevelSkinConductanceAvg: Int = 0
let slots = List<MetricEmographySummarySlot>()
}
Notes
- Summaries are always for a full day from 00:00 till 29:59 local phone time.
- stressLevelSkinConductanceAvg -> Under development
EDA / Emography summary slots (V2 bracelet ONLY)
Data structure
public final class MetricEmographySummarySlot : Object {
@objc public dynamic var date: Date = Date()
@objc public dynamic var cognitiveZone: Int = 0
@objc public dynamic var predictiveCZ: Int = 0
@objc public dynamic var predictiveCZTransitionTime: Int = 0
@objc public dynamic var czHour: Int = 0
@objc public dynamic var cortisolContribution: Int = 0
@objc public dynamic var cognitiveZoneQ: Int = 0
@objc public dynamic var stressLevelSkinConductance: Int = 0
@objc public dynamic var stressLevelSkinConductanceQ: Int = 0
}
Sleep summary
The SDK has two parallel sleep summary models — Philips (MetricSleepSummery, the legacy pipeline) and Corsano (MetricCorsanoSleepSummery, the current pipeline). Both Realm tables are populated on every BLE sync; which one is authoritative for cloud sync and the local "sleep processed" notification is resolved by the SDK's internal SleepPipeline helper — forced to Corsano on the CorsanoSDK_NoCloud build, or selected by the syncCorsanoSleepSummary UserDefaults flag on the full CorsanoSDK build. See Developer guide → Two sleep pipelines for details.
Philips data structure (MetricSleepSummery)
public final class MetricSleepSummery: Object {
@objc dynamic var id: Int = 0
@objc dynamic var recordDate: String = ""@objc dynamic var startSession: Date = Date()
@objc dynamic var stopSession: Date = Date()
@objc dynamic var tzOffset: Int = 0
@objc dynamic var totalSleepDuration: Int = 0
@objc dynamic var timeToSleep: Int = 0
@objc dynamic var timeToWakeUp: Int = 0
@objc dynamic var consecutiveSleepSlotLightToRemove: Int = 0
@objc dynamic var consecutiveSleepSlotDeepToRemove: Int = 0
@objc dynamic var consecutiveSleepSlotRemToRemove: Int = 0
@objc dynamic var totalSecsLight: Int = 0
@objc dynamic var totalSecsDeep: Int = 0
@objc dynamic var totalSecsAwake: Int = 0
@objc dynamic var totalSecsREM: Int = 0
@objc dynamic var totalSecsDept: Int = 0
@objc dynamic var totalSecsIntensityAdjust: Int = 0
@objc dynamic var totalSecsDeptAdjust: Int = 0
@objc dynamic var totalDeepToLightTransitions: Int = 0
@objc dynamic var totalDeepToREMTransitions: Int = 0
@objc dynamic var totalLightToDeepTransitions: Int = 0
@objc dynamic var totalLightToREMTransitions: Int = 0
@objc dynamic var totalREMToDeepTransitions: Int = 0
@objc dynamic var totalREMToLightTransitions: Int = 0
@objc dynamic var totalSleepToAwakeTransitions: Int = 0
@objc dynamic var totalAwakeToSleepTransitions: Int = 0
@objc dynamic var readyForSync: Bool = false
var slots = List<MetricSleepSummerySlots>()
}
Notes
- Summaries are always for one sleep session
- totalSleepToAwakeTransitions -> Awake count
Sleep summary slots
Data structure
public final class MetricSleepSummerySlots: Object {
@objc dynamic var timestamp: String = ""
@objc dynamic var date: Date = Date()
@objc dynamic var sleepType: Int = 0
@objc dynamic var isRejected: Bool = false
@objc dynamic var isEdited: Bool = false
}
Sleep types
| SleepType | Definition |
|---|---|
| 0 | Undefined |
| 1 | Awake sleep |
| 2 | Light sleep |
| 3 | Deep sleep |
| 4 | REM sleep |
Corsano sleep summary (recommended)
The Corsano pipeline mirrors the Philips one field-for-field — same getter surface (getTotalSleepDuration(), getTotalSecsLight(), getLightSleepDurationPercentage(), getWeeklyAvgSleepData(data:), …) — but is produced by a different on-device algorithm and is persisted to a separate Realm table.
MetricCorsanoSleepSummery
public final class MetricCorsanoSleepSummery: Object {
@Persisted public var id: Int = 0
@Persisted public var recordDate: String = ""
@Persisted public var startSession: Date = Date()
@Persisted public var stopSession: Date = Date()
@Persisted public var startSessionOriginalPH: Date? // Philips original (for compare)
@Persisted public var stopSessionOriginalPH: Date? // Philips original (for compare)
@Persisted public var tzOffset: Int = 0
@Persisted public var totalSleepDuration: Int = 0
@Persisted public var timeToSleep: Int = 0
@Persisted public var timeToWakeUp: Int = 0
@Persisted public var consecutiveSleepSlotLightToRemove: Int = 0
@Persisted public var consecutiveSleepSlotDeepToRemove: Int = 0
@Persisted public var consecutiveSleepSlotRemToRemove: Int = 0
@Persisted public var totalSecsLight: Int = 0
@Persisted public var totalSecsDeep: Int = 0
@Persisted public var totalSecsAwake: Int = 0
@Persisted public var totalSecsREM: Int = 0
@Persisted public var totalSecsDept: Int = 0
@Persisted public var totalSecsIntensityAdjust: Int = 0
@Persisted public var totalSecsDeptAdjust: Int = 0
@Persisted public var totalDeepToLightTransitions: Int = 0
@Persisted public var totalDeepToREMTransitions: Int = 0
@Persisted public var totalLightToDeepTransitions: Int = 0
@Persisted public var totalLightToREMTransitions: Int = 0
@Persisted public var totalREMToDeepTransitions: Int = 0
@Persisted public var totalREMToLightTransitions: Int = 0
@Persisted public var totalSleepToAwakeTransitions: Int = 0
@Persisted public var totalAwakeToSleepTransitions: Int = 0
@Persisted public var readyForSync: Bool = false
@Persisted public var isEdited: Bool = false // user edited the bracket
@Persisted public var cloudResultFailure: Bool = false
@Persisted public var sleepRHR: Int = 0
@Persisted public var sleepRHRQuality: Int = 0
@Persisted public var slots: List<MetricCorsanoSleepSummerySlots>
}
Retrieval (observer-based, mirrors the Philips API):
sleepApi.getNewCorsanoSleepSummeryFor(date) { summery, change in
// summery: MetricCorsanoSleepSummery?
}
sleepApi.getNewCorsanoLastSevenDaysSleepSummaryFrom(date) { summeries, change in
// summeries: [MetricCorsanoSleepSummery]
}
MetricCorsanoSleepSummerySlots
Same shape as the Philips slots: one row per minute of the recorded session.
public final class MetricCorsanoSleepSummerySlots: Object {
@Persisted public var timestamp: String = ""
@Persisted public var date: Date = Date()
@Persisted public var sleepType: Int = 0
@Persisted public var isRejected: Bool = false
@Persisted public var isEdited: Bool = false
}
Sleep types are the same 0–4 numbering as the Philips slots. The Corsano stream also carries four marker values on top of those — these are persisted to the slots table (mapped to sleepType = 1 for chart rendering) but are filtered out of the summary stage totals by SleepStageDebug(rawValue:):
| Marker (raw) | Meaning |
|---|---|
| 101 | GoToBed |
| 102 | SleepOnset |
| 103 | Wake |
| 104 | OutOfBed |
CorsanoSleepSessions — bedtime bracket
A separate Realm table that exposes the four reference timestamps of the session. Cheap synchronous lookup — handy for rendering the "Go to bed → Out of bed" bracket without walking the slots list.
public final class CorsanoSleepSessions: Object {
@Persisted public var id: Int = 0
@Persisted public var recordDate: String = ""
@Persisted public var tzOffset: Int = 0
@Persisted public var bedTime: Date = Date() // 101 marker
@Persisted public var sleepOnSet: Date = Date() // 102 marker
@Persisted public var wakeUpTime: Date = Date() // 103 marker
@Persisted public var outOfBed: Date = Date() // 104 marker
}
Retrieval:
let session: CorsanoSleepSessions? = sleepApi.getCorsanoSleepSummeryFor(date)
Editing a Corsano sleep session
When the user manually adjusts the bedtime bracket on the host UI, push the change through this API rather than writing to the Realm models directly — it re-computes the summary slots, pads the raw stream with synthetic light-sleep fillers if the user extended beyond the bracelet samples, and flags the row isEdited = true so subsequent BLE syncs don't clobber it:
sleepApi.updateCorsanoSleepSession(
recordDate: "2026-05-18",
bedTime: Date(...),
outOfBed: Date(...)
)