Important Notes
New features were added in v3.1.7, including audio route handling and audio session option customization.
Audio Route Change Handling
1. New Delegate Methods
New delegate methods are available to handle audio route changes (Bluetooth connection/disconnection, headphone connection, etc.).
protocol AsleepSleepTrackingManagerDelegate {
// Called before route change (before Engine restart)
func willChangeAudioRoute(from oldRoute: AVAudioSessionRouteDescription,
to newRoute: AVAudioSessionRouteDescription)
// Called after route change (after Engine restart)
func didChangeAudioRoute(to newRoute: AVAudioSessionRouteDescription)
}Features:
- Optional methods (existing behavior maintained if not implemented)
- Called when Bluetooth/headphones are connected or disconnected
- Separate calls before and after Engine reconfiguration
2. Interruption vs Route Change
| Delegate | Trigger Condition | Engine Restart | Processing Speed |
|---|---|---|---|
didInterrupt() / didResume() | Phone call, alarm, VOIP | No | Fast (~few milliseconds) |
willChangeAudioRoute() / didChangeAudioRoute() | Bluetooth/headphone connection/disconnection | Yes | Slow (100-500ms) |
3. Usage Examples
Music Playback Apps
extension YourViewController: AsleepSleepTrackingManagerDelegate {
func willChangeAudioRoute(from oldRoute: AVAudioSessionRouteDescription,
to newRoute: AVAudioSessionRouteDescription) {
// Pause music when route change starts
if musicPlayer?.isPlaying == true {
musicPlayer?.pause()
}
}
func didChangeAudioRoute(to newRoute: AVAudioSessionRouteDescription) {
// Resume music after route change completes
musicPlayer?.play()
}
}Route Information Logging
func willChangeAudioRoute(from oldRoute: AVAudioSessionRouteDescription,
to newRoute: AVAudioSessionRouteDescription) {
let oldOutput = oldRoute.outputs.first?.portType.rawValue ?? "unknown"
let newOutput = newRoute.outputs.first?.portType.rawValue ?? "unknown"
print("Audio route changing: \(oldOutput) -> \(newOutput)")
}Audio Session Management
Design Purpose
Starting from v3.1.7, the SDK fully manages the audio session. If clients directly call AVAudioSession.setActive() or setCategory(), conflicts with the SDK may occur. Required options should be passed to startTracking() so the SDK can apply them at the appropriate time.
1. API Changes
You can now pass audio session options to the startTracking() method.
// Existing (still works)
manager?.startTracking()
// New: Pass additional options
manager?.startTracking(additionalAudioSessionOptions: [.duckOthers])Important:
- Do NOT call
AVAudioSession.sharedInstance().setActive()directly during tracking - Do NOT call
AVAudioSession.sharedInstance().setCategory()directly during tracking - Pass only required options via
additionalAudioSessionOptions
2. SDK Default Configuration
The SDK configures the audio session as follows:
// Category (cannot be changed)
.playAndRecord // Supports simultaneous recording and playback
// Options (cannot be changed)
[.mixWithOthers, .allowBluetoothA2DP]Options passed via additionalAudioSessionOptions are added to the defaults:
manager?.startTracking(additionalAudioSessionOptions: [.duckOthers])
// Final configuration: Category = .playAndRecord, Options = [.mixWithOthers, .allowBluetoothA2DP, .duckOthers]3. Main Use Cases
Automatically Reduce Music Volume
func startTrackingWithMusic() {
// Automatically reduce volume of other app's audio
manager?.startTracking(additionalAudioSessionOptions: [.duckOthers])
// Play music
musicPlayer?.play()
}AirPlay Support
func startTrackingWithAirPlay() {
// Allow AirPlay
manager?.startTracking(additionalAudioSessionOptions: [.allowAirPlay])
}Multiple Options Combined
manager?.startTracking(additionalAudioSessionOptions: [
.duckOthers, // Reduce music volume
.allowAirPlay // Allow AirPlay
])4. Available Options
| Option | Description | Use Case |
|---|---|---|
.mixWithOthers | Mix with other apps' audio (SDK default) | Always applied |
.allowBluetoothA2DP | Allow A2DP Bluetooth (SDK default) | Always applied |
.duckOthers | Reduce other apps' audio volume | Minimize interruption during music playback |
.allowAirPlay | Allow AirPlay | HomePod playback |
.defaultToSpeaker | Prioritize speaker output | Use speakerphone |
Note: .mixWithOthers is a SDK default, so no need to pass it separately.
5. Important Notes
Option Accumulation:
- Options are reset with each
startTracking()call - Previous options are not retained
// First call
manager?.startTracking(additionalAudioSessionOptions: [.duckOthers])
// After stopTracking(), restart
manager?.stopTracking()
manager?.startTracking(additionalAudioSessionOptions: [.allowAirPlay])
// -> .duckOthers is removed, only .allowAirPlay is appliedIncorrect Usage Example:
// [DO NOT] Direct audio session control during tracking
func startTracking() {
manager?.startTracking()
// Conflicts with SDK!
try? AVAudioSession.sharedInstance().setActive(true) // <- Don't do this
}Stability Improvements (v3.1.7)
1. Bluetooth Stability Improvements
Fixed Issues:
- Recording interruption during Bluetooth connection/disconnection
- Recording resume failure after VOIP calls
- Crashes during audio format changes while Bluetooth is connected
Results:
- Stable recording maintained even during Bluetooth device connection state changes
- Automatic switching between built-in mic and Bluetooth
2. Interruption Recovery Improvements
Automatic Recovery Mechanisms:
- Added 10-second automatic recovery timer
- Automatic recovery attempt when app returns to foreground
- VOIP detection and waiting
No Code Changes Required:
- Handled automatically within SDK
- Existing
didResume()delegate still called as before
3. Crash Fixes
Fixed crashes in the following scenarios:
- Audio route reconfiguration during interruptions
- Memory access errors
- Race conditions
Migration Guide
Existing Code (No Changes Required)
class MyViewController: UIViewController, AsleepSleepTrackingManagerDelegate {
var manager: Asleep.SleepTrackingManager?
func startTracking() {
Asleep.shared().createSleepTrackingManager(config: config, delegate: self)
manager?.startTracking() // Existing code works as-is
}
// Existing delegate methods
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) { }
}Optional Enhancement 1: Music Playback Control
extension MyViewController {
// Add new delegates (optional)
func willChangeAudioRoute(from oldRoute: AVAudioSessionRouteDescription,
to newRoute: AVAudioSessionRouteDescription) {
if musicPlayer?.isPlaying == true {
musicPlayer?.pause()
}
}
func didChangeAudioRoute(to newRoute: AVAudioSessionRouteDescription) {
musicPlayer?.play()
}
}Optional Enhancement 2: Add Audio Options
extension MyViewController {
func startTrackingWithMusic() {
Asleep.shared().createSleepTrackingManager(config: config, delegate: self)
// Automatically reduce music volume
manager?.startTracking(additionalAudioSessionOptions: [.duckOthers])
// Play background music
playBackgroundMusic()
}
}Updated 6 days ago
