SleepTrackingManager

Create manager

Asleep.createSleepTrackingManager()

var config: Asleep.Config?
let delegate: AsleepSleepTrackingManagerDelegate = self
var manager: Asleep.SleepTrackingManager?

if let config {
    manager = Asleep.createSleepTrackingManager(config: config,
                                                delegate: delegate)
}
Property NameTypeDescription
configAsleep.ConfigEnter the Asleep.Config instance
delegateAsleepSleepTrackingManagerDelegateDelegate to receive results and errors

AsleepSleepTrackingManagerDelegate

protocol AsleepSleepTrackingManagerDelegate {
    func didCreate()
    func didUpload(sequence: Int)
    func didClose(sessionId: String)
    func didFail(error: Asleep.AsleepError)
    func didInterrupt()
    func didResume()
    func micPermissionWasDenied()
    func analysing(session: Asleep.Model.Session)
}
  1. didCreate()
    Tracking is generated.

  2. didUpload()
    Data is uploaded.

    Property NameTypeDescription
    sequenceIntA value that starts from 0 and increases by 1 every 30 seconds
  3. didClose()
    Tracking is terminated.

    Property NameTypeDescription
    sessionIdStringReport result id value
  4. didFail()
    Due to error, tracking is ended

    Property NameTypeDescription
    errorAsleep.AsleepErrorError Codes
  5. didInterrupt()
    Tracking is interrupted due to events such as calls.

  6. didResume()
    Tracking resumes once the interrupting factor is resolved.

  7. micPermissionWasDenied()
    Tracking cannot be started without microphone permission.

  8. analysing()
    Latest session data

    Property NameTypeDescription
    sessionAsleep.Model.Sessionduring analysis, tracking session information



📘

AsleepCompletableTrackingDelegate has been added

Passing AsleepSleepTrackingManagerDelegate to the existing createSleepTrackingManager(config:delegate:) keeps working as before.

The delegate type determines the notifications, and recordingPath determines the recording files.

delegaterecordingPathAnalysis completion notification (didComplete)Recording file
AsleepSleepTrackingManagerDelegate (existing)-XX
AsleepCompletableTrackingDelegateomitted (nil)OX
AsleepCompletableTrackingDelegatefolder URLOO

AsleepCompletableTrackingDelegate


var config: Asleep.Config?
let delegate: AsleepCompletableTrackingDelegate = self
let recordingPath: URL?                 
let recordingType: Asleep.RecordingType
var manager: Asleep.SleepTrackingManager?

if let config {
    manager = Asleep.createSleepTrackingManager(config: config,
                                                delegate: delegate,
                                                recordingPath: recordingPath,
                                                recordingType: recordingType)
}
Property NameTypeDescription
configAsleep.ConfigEnter the Asleep.Config instance
delegateAsleepCompletableTrackingDelegateDelegate to receive results and errors
recordingPathURL?The folder where recording files are saved. If it is omitted or nil, no recording file is created. Default nil
recordingTypeAsleep.RecordingTypeThe type of recording to keep. Applied only when recordingPath is set. Default .all

It inherits the existing AsleepSleepTrackingManagerDelegate and adds two methods.

protocol AsleepCompletableTrackingDelegate: AsleepSleepTrackingManagerDelegate {
    func didCreate(sessionId: String)
    func didComplete(session: Asleep.Model.Session)
}
  • didCreate(sessionId:) — Called with the session id once the session is created. It is called instead of the existing didCreate(), so you do not need to implement didCreate().
    • sessionId: Generated session id
  • didComplete(session:) — Called when the server analysis is finished after tracking is terminated and the result is ready to be retrieved.
    • session: The session whose analysis is complete. You can display the result right away with sleepStages and so on, and call Reports.report(sessionId:) if you also need the statistics (stat).

The other methods (didUpload, didClose, didFail, didInterrupt, didResume, micPermissionWasDenied, analysing) are the same as before.


From termination to analysis completion

When stopTracking() is called, the SDK closes the session first and then checks periodically whether the analysis is finished.

stopTracking()
  │
  ├ ① Session close request
  │     Success → didClose(sessionId:)
  │
  ├ ② Analysis completion check (every 3 seconds · up to 10 times, about 30 seconds)
  │
  ├ ③-a Analysis complete → didComplete(session:)
  │
  └ ③-b Not finished within 30 seconds → didFail(error: .completeTimeout)
🚧

completeTimeout is not a tracking failure

The session has already been terminated normally; it only means the analysis did not finish within 30 seconds. Retrieve the result a little later with Reports.report(sessionId:).

If the session close request itself fails, only didFail(error:) is called, and didClose and the analysis completion check do not proceed.


Saving recording files (optional)

If you set recordingPath, the audio is temporarily saved in 30-second units during tracking, and once the analysis is complete, only the snoring and unstable-breathing segment files selected based on the analysis result are kept in {recordingPath}/audio/{sessionId}/.

ItemDescription
Files keptTop 10 by intensity among the detected snoring segments + top 10 by severity among the detected unstable-breathing segments
File formatM4A (AAC), 16kHz, mono, 30 seconds
Number of sessions keptThe 7 most recent (when exceeded, the oldest sessions are deleted first)
BackupExcluded from iCloud / iTunes backup
Storage spaceIf the free space is less than 200MB when tracking starts, didFail(error: .insufficientStorage) is called and tracking does not start. If it drops below 10MB during tracking, only the file saving is skipped and tracking continues

Asleep.RecordingType

enum RecordingType {
    case all          // Snoring + unstable breathing (default)
    case snoringOnly  // Snoring segments only
    case breathOnly   // Unstable-breathing segments only
}
  • recordingType does not turn on recordings for metrics that your plan does not provide. It is used to narrow the type within the scope of your plan.
  • If the analysis completion is not received (completeTimeout), .all keeps every file saved during tracking without selecting any, and .snoringOnly / .breathOnly keep nothing.
  • Sessions whose analysis result cannot be used, such as when the tracking time is too short, do not keep recording files.

Asleep.createRecordingFileManager()

Retrieves or deletes the saved recording files. You must pass the same recordingPath that was passed to createSleepTrackingManager. If you pass a different path, the result is empty.

let fileManager = Asleep.createRecordingFileManager(recordingPath: recordingPath)

// [String]
let sessionIds = fileManager.getSessions()

// Snoring files (in order of intensity) 
let snoringFiles = fileManager.getSnoringFiles(sessionId: sessionId) 

// Unstable-breathing files (in order of severity)
let breathFiles = fileManager.getBreathFiles(sessionId: sessionId)

// All 30-second segments (in order of segment index) 
let segments = fileManager.getAllSegments(sessionId: sessionId) 

try fileManager.deleteSession(sessionId: sessionId)
try fileManager.deleteAllSessions()
// Asleep.Model.RecordingFile
struct RecordingFile {
    let filePath: URL?        // nil for segments whose file was not saved
    let segmentIndex: Int     // 30-second segment index
    let maxDb: Float
    let isSnoringDetected: Bool
    let isBreathDetected: Bool
    let timestamp: String?
    let snoreIntensity: Float
    let breathSeverity: Float
}

Concurrent Audio Capture (optional)

Asleep.SleepTrackingManager.setDeliveryDelegate()

let trackingManager = Asleep.createSleepTrackingManager(config: config, delegate: delegate)
trackingManager.setDeliveryDelegate(self)

Asleep.AsleepAudioDeliveryManagerDelegate

protocol AsleepAudioDeliveryManagerDelegate: AnyObject {
    func deliveryAudio(didReceiveRawBuffer buffer: AVAudioPCMBuffer, 
										   at time: AVAudioTime)
}
🚧

Use only when simultaneous SDK–client recording is required

This function is used to comply with Apple’s recommendation of maintaining a single Audio Session in iOS,
while ensuring stable audio recording in scenarios where both the SDK and the client need to handle audio simultaneously.
It is specifically designed to deliver PCM data to the client in real time at the InputNode level.


Start sleep tracking

Asleep.SleepTrackingManager.startTracking()

var manager: Asleep.SleepTrackingManager?
manager?.startTracking()
🚧

Follow the guideline for testing the sleep tracking

To accurately test Asleep's sleep tracking/analysis, please follow the test environment guide. Please note that sleep analysis results obtained in environments not adhering to this guide may not accurately reflect actual sleep patterns.

🔗 Check Test Environment Guideline


Request the latest analyzed sleep data

Asleep.SleepTrackingManager.requestAnalysis()

var manager: Asleep.SleepTrackingManager?
manager?.requestAnalysis()

Stop sleep tracking

Asleep.SleepTrackingManager.stopTracking()

var manager: Asleep.SleepTrackingManager?
manager?.stopTracking()

Specifying the analysis range (v3.3.0)

When you stop tracking, you can directly specify the range (start and end time) that the server will analyze. For example, use it when you want to analyze only the "time the user fell asleep ~ time the user woke up" entered by the user.

var manager: Asleep.SleepTrackingManager?

// Analyze the whole tracking (existing)
manager?.stopTracking()

// Analyze only the specified range
manager?.stopTracking(analysisStartTime: analysisStartTime,
                      analysisEndTime: analysisEndTime)
Property NameTypeDescription
analysisStartTimeDateAnalysis start time
analysisEndTimeDateAnalysis end time. Must be later than analysisStartTime
  • Always pass the two values together. To terminate without a range, call stopTracking().
  • The flow after termination (didClose → didComplete) is the same as when no range is specified.

What changes when a range is specified

ItemChange
sleepStages / breathStages / snoringStagesOnly the specified range is returned
Session.startTime / endTimeThe start and end time of the analysis range
Session.measurementStartTime / measurementEndTimeThe time at which the tracking (recording) actually started and ended
Report.analysisInformation about the analyzed range (see the Reports page)
Recording file (when recordingPath is set)Files are selected from within the analysis range

When an invalid range is passed

So that the session is not left open even if the range is invalid, the SDK terminates the session without the range.

CaseBehavior
The start time is later than or equal to the end timedidFail(error: .invalidParameter) is called, and then the session is terminated without the range. didClose → didComplete then proceed normally
The server rejects the rangeThe SDK requests the termination again without the range. If it succeeds, the normal flow proceeds and the app is not notified separately
The termination request itself failsdidFail(error:), same as before

You can check whether the range was actually applied by looking at Report.analysis in the result, or by comparing Session.startTime with measurementStartTime.



Get sleep tracking status

Asleep.SleepTrackingManager.getTrackingStatus()

var manager: Asleep.SleepTrackingManager?
let trackingStatus = manager?.getTrackingStatus()

Resume tracking

Asleep.SleepTrackingManager.resumeTracking()

  • If a cannotActivateInBackground error occurs during sleep tracking, notify the user (e.g., through a notification), and run Asleep.SleepTrackingManager.resumeTracking() while the app is in the foreground.
var manager: Asleep.SleepTrackingManager?
manager?.resumeTracking()

Data Type

Asleep.SleepTrackingManager.TrackingStatus

struct TrackingStatus {
    var sessionId: String?
}
Property nameTypeDescription
sessionIdString?Currently tracking session id
Available from the didCreate function of AsleepSleepTrackingManagerDelegate
Valid until the corresponding Session is closed

Asleep.Model.Session

struct Session {
    let id: String
    let state: State
    let startTime: Date
    let endTime: Date?
    let unexpectedEndTime: Date?
    let measurementStartTime: Date?
    let measurementEndTime: Date?
    let createdTimezone: String
    let sleepStages: [Int]?
    let breathStages: [Int]?
    let snoringStages: [Int]?
    let sleepStageProbs: [[Float]]?
    let breathStageProbs: [[Float]]?
    let snoringStageProbs: [[Float]]?
}

enum State {
    case open
    case closed
    case complete
}
Property nameTypeDescriptionVersion
idStringSleep session id
stateAsleep.Model.StateSleep session state (OPEN, CLOSED, or COMPLETE)
startTimeDateSession start time. If an analysis period was specified, the start time of the analysis period
endTimeDate?Session end time. If an analysis period was specified, the end time of the analysis period
unexpectedEndTimeDate?If a session fails to proceed and terminate properly due to app crashes or similar issues, and the client later executes initConfig to terminate the session, the time at which this happens is recorded. In this case, the "end_time" is calculated based on the sequence number of the last uploaded audio file. Therefore, if "end_time" is not null, it indicates an abnormal session.
measurementStartTimeDate?The time the measurement (recording) actually started, regardless of the analysis periodv3.3.0
measurementEndTimeDate?The time the measurement (recording) actually ended, regardless of the analysis periodv3.3.0
createdTimezoneStringTimezone of session creation (Timezone List)
sleepStagesArray<Int>Sleep stages
-1: error
0 : wake
1 : light
2 : deep
3 : rem
breathStagesArray<Int>Breathing stability stages
snoringStagesArray<Int>Snoring stages
-1: error
0 : no snoring
1 : snoring
sleepStageProbs[[Float]]?Probability values of the sleep stages for each epoch
breathStageProbs[[Float]]?Probability values of the breathing stability stages for each epoch
snoringStageProbs[[Float]]?Probability values of the snoring stages for each epoch

Did this page help you?