Files
gh-asleep-ai-sleeptrack-ski…/skills/sleeptrack-ios/SKILL.md
2025-11-29 17:58:23 +08:00

15 KiB

name, description
name description
sleeptrack-ios This skill helps iOS developers integrate the Asleep SDK for sleep tracking functionality. Use this skill when building native iOS apps with Swift/SwiftUI that need sleep tracking capabilities, implementing delegate patterns, configuring iOS permissions (microphone, notifications, background modes), managing tracking lifecycle, integrating Siri Shortcuts, or working with Combine framework for reactive state management.

Sleeptrack iOS

Overview

This skill provides comprehensive guidance for integrating the Asleep SDK into native iOS applications using Swift and SwiftUI. It covers SDK setup, iOS-specific permissions, delegate-based architecture, tracking lifecycle management, Combine framework integration, and Siri Shortcuts support.

Use this skill when:

  • Building native iOS sleep tracking applications
  • Implementing SwiftUI-based tracking interfaces
  • Managing iOS permissions and background modes
  • Working with delegate patterns for SDK callbacks
  • Integrating Siri Shortcuts for voice-activated tracking
  • Using Combine framework for reactive state management

Prerequisites: Developers should first review the sleeptrack-foundation skill to understand core Asleep concepts, authentication, data structures, and error handling before implementing iOS-specific integration.

Quick Start

1. Installation

Add AsleepSDK to your Xcode project using Swift Package Manager:

// In Xcode: File → Add Packages
// Enter package URL: https://github.com/asleep-ai/asleep-sdk-ios

Or add to Package.swift:

dependencies: [
    .package(url: "https://github.com/asleep-ai/asleep-sdk-ios", from: "2.0.0")
]

2. Configure iOS Permissions

Add required permissions to Info.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- Microphone access for audio-based sleep tracking -->
    <key>NSMicrophoneUsageDescription</key>
    <string>This app uses your microphone to track sleep stages and detect snoring during sleep.</string>

    <!-- Background audio mode for continuous tracking -->
    <key>UIBackgroundModes</key>
    <array>
        <string>audio</string>
    </array>

    <!-- Optional: For notification reminders -->
    <key>NSUserNotificationsUsageDescription</key>
    <string>Get reminders to start and stop sleep tracking.</string>
</dict>
</plist>

3. Basic Setup

import SwiftUI
import AsleepSDK

@main
struct SleepTrackerApp: App {
    var body: some Scene {
        WindowGroup {
            MainView()
        }
    }
}

SDK Architecture

The Asleep iOS SDK follows a delegate-based architecture with three main components:

1. AsleepConfig - Configuration and User Management

Purpose: Initialize SDK with API credentials and manage user lifecycle.

Key Delegate: AsleepConfigDelegate

protocol AsleepConfigDelegate {
    func userDidJoin(userId: String, config: Asleep.Config)
    func didFailUserJoin(error: Asleep.AsleepError)
    func userDidDelete(userId: String)
}

2. SleepTrackingManager - Tracking Lifecycle

Purpose: Control sleep tracking start, stop, and monitor session state.

Key Delegate: AsleepSleepTrackingManagerDelegate

protocol AsleepSleepTrackingManagerDelegate {
    func didCreate()                          // Session created
    func didUpload(sequence: Int)             // Data uploaded
    func didClose(sessionId: String)          // Tracking stopped
    func didFail(error: Asleep.AsleepError)  // Error occurred
    func didInterrupt()                       // Interrupted (e.g., phone call)
    func didResume()                          // Resumed after interruption
    func micPermissionWasDenied()             // Mic permission denied
    func analysing(session: Asleep.Model.Session) // Real-time data (optional)
}

3. Reports - Retrieving Sleep Data

Purpose: Fetch sleep reports and session lists after tracking completes.

// Reports API is async/await based, not delegate-driven
let reports = Asleep.createReports(config: config)

// Get single report
let report = try await reports.report(sessionId: "session_id")

// Get report list
let reportList = try await reports.reports(
    fromDate: "2024-01-01",
    toDate: "2024-01-31"
)

Implementation Overview

Minimal ViewModel Example

import Foundation
import Combine
import AsleepSDK

final class SleepTrackingViewModel: ObservableObject {
    private(set) var trackingManager: Asleep.SleepTrackingManager?
    private(set) var reports: Asleep.Reports?

    @Published var isTracking = false
    @Published var error: String?
    @Published private(set) var config: Asleep.Config?

    func initAsleepConfig(apiKey: String, userId: String) {
        Asleep.initAsleepConfig(
            apiKey: apiKey,
            userId: userId,
            delegate: self
        )
    }

    func startTracking() {
        trackingManager?.startTracking()
    }

    func stopTracking() {
        trackingManager?.stopTracking()
    }
}

// Implement delegates
extension SleepTrackingViewModel: AsleepConfigDelegate {
    func userDidJoin(userId: String, config: Asleep.Config) {
        Task { @MainActor in
            self.config = config
            self.trackingManager = Asleep.createSleepTrackingManager(
                config: config,
                delegate: self
            )
        }
    }

    func didFailUserJoin(error: Asleep.AsleepError) {
        Task { @MainActor in
            self.error = error.localizedDescription
        }
    }

    func userDidDelete(userId: String) {
        // Handle user deletion
    }
}

extension SleepTrackingViewModel: AsleepSleepTrackingManagerDelegate {
    func didCreate() {
        Task { @MainActor in
            self.isTracking = true
        }
    }

    func didClose(sessionId: String) {
        Task { @MainActor in
            self.isTracking = false
            // Initialize reports to fetch session data
            self.reports = Asleep.createReports(config: config!)
        }
    }

    func didFail(error: Asleep.AsleepError) {
        Task { @MainActor in
            self.error = error.localizedDescription
        }
    }

    // Implement other delegate methods as needed
}

For complete ViewModel implementation with all delegate methods, see references/complete_viewmodel_implementation.md

iOS-Specific Features

1. Siri Shortcuts

Enable voice-activated tracking with App Intents (iOS 16+). Users can say "Hey Siri, start sleep" or "Hey Siri, stop sleep".

For complete Siri Shortcuts implementation, see references/ios_specific_features.md

2. Background Audio Mode

Configure background audio to maintain tracking during sleep. Simply add audio to UIBackgroundModes in Info.plist - iOS handles the rest automatically.

For details, see references/ios_specific_features.md

3. Microphone Permission

Request microphone permission before starting tracking:

import AVFoundation

func requestMicrophonePermission() async -> Bool {
    switch AVAudioSession.sharedInstance().recordPermission {
    case .granted: return true
    case .denied: return false
    case .undetermined:
        return await AVAudioSession.sharedInstance().requestRecordPermission()
    @unknown default: return false
    }
}

For complete permission handling, see references/ios_specific_features.md

4. App Lifecycle Management

Handle app state transitions using SwiftUI's scenePhase:

struct SleepTrackingView: View {
    @Environment(\.scenePhase) private var scenePhase

    var body: some View {
        // ... view content ...
        .onChange(of: scenePhase) { newPhase in
            switch newPhase {
            case .active: print("App is active")
            case .inactive: print("App is inactive")
            case .background: print("App in background - tracking continues")
            @unknown default: break
            }
        }
    }
}

For advanced lifecycle patterns, see references/ios_specific_features.md

5. Persistent Storage

Store configuration using AppStorage:

struct SleepTrackingView: View {
    @AppStorage("sleepapp+apikey") private var apiKey = ""
    @AppStorage("sleepapp+userid") private var userId = ""
    // Values automatically persist across app launches
}

Error Handling

Common Error Patterns

func handleError(_ error: Asleep.AsleepError) {
    switch error {
    case .micPermission:
        // Guide user to Settings
        showMicPermissionAlert()

    case .audioSessionError:
        // Another app is using microphone
        showAudioUnavailableAlert()

    case let .httpStatus(code, _, message):
        switch code {
        case 403: // Session already active on another device
        case 404: // Session not found
        default: break
        }

    default:
        showGenericError(error.localizedDescription)
    }
}

Retry with Exponential Backoff

func startTrackingWithRetry() {
    trackingManager?.startTracking()
}

func didFail(error: Asleep.AsleepError) {
    if isTransientError(error) && retryCount < maxRetries {
        retryCount += 1
        DispatchQueue.main.asyncAfter(deadline: .now() + pow(2.0, Double(retryCount))) {
            self.startTrackingWithRetry()
        }
    } else {
        handleError(error)
    }
}

For comprehensive error handling patterns, see references/advanced_patterns.md

Best Practices

1. State Management

Use @Published properties for reactive UI updates:

final class SleepTrackingViewModel: ObservableObject {
    @Published var isTracking = false
    @Published var error: String?
    // UI automatically updates when values change
}

2. Main Thread Safety

Always update UI on main thread:

func didCreate() {
    Task { @MainActor in  // Ensures main thread
        self.isTracking = true
    }
}

3. Resource Cleanup

final class SleepTrackingViewModel: ObservableObject {
    deinit {
        trackingManager = nil
        reports = nil
    }
}

4. User Experience

Provide clear visual feedback with loading states, progress indicators, and error messages. Disable controls appropriately during tracking.

5. Testing Considerations

Use dependency injection for testable code:

protocol SleepTrackingManagerProtocol {
    func startTracking()
    func stopTracking()
}

// Production and mock implementations

For complete testing patterns, see references/advanced_patterns.md

Common Integration Patterns

Pattern 1: Simple Single-View App

Best for basic sleep tracking with minimal features. Single view with tracking controls.

Pattern 2: Multi-View App with Navigation

Best for apps with reports, settings, and history. Uses TabView for navigation between Track, History, and Settings.

Pattern 3: Centralized SDK Manager

Best for complex apps sharing SDK instance across views. Single source of truth with AsleepSDKManager.shared.

For complete implementation of all patterns, see references/advanced_patterns.md

Real-time Data Access

Access preliminary sleep data during tracking (available after sequence 10):

func analysing(session: Asleep.Model.Session) {
    Task { @MainActor in
        if let sleepStages = session.sleepStages {
            updateRealtimeChart(stages: sleepStages)
        }
    }
}

func didUpload(sequence: Int) {
    // Real-time data available every 10 sequences after sequence 10
    if sequence >= 10 && sequence % 10 == 0 {
        // SDK automatically calls analysing() delegate
    }
}

Fetching Reports

Retrieve sleep session data after tracking:

func fetchReport(sessionId: String) async {
    do {
        let report = try await reports?.report(sessionId: sessionId)
        // Process report data
    } catch {
        // Handle error
    }
}

// Fetch multiple sessions
func fetchReportList() async {
    let reportList = try await reports?.reports(
        fromDate: "2024-01-01",
        toDate: "2024-01-31"
    )
}

Troubleshooting

Tracking Doesn't Start

Causes: Missing microphone permission, empty API key/user ID, another app using microphone

Solution: Validate configuration and check microphone permission before starting

Background Tracking Stops

Causes: Background audio mode not configured, memory pressure, force-closed app

Solution: Ensure UIBackgroundModes includes audio in Info.plist

Reports Not Available

Causes: Session processing incomplete (takes time), minimum duration not met (5 minutes), network issues

Solution: Implement retry logic with exponential backoff when fetching reports

For detailed troubleshooting, see the complete implementation examples in references/

Sample Code Reference

This skill is based on the official Asleep iOS sample app:

  • MainViewModel.swift: Complete ViewModel with all delegates
  • MainView.swift: SwiftUI view with tracking controls
  • StartSleepIntent.swift / StopSleepIntent.swift: Siri Shortcuts
  • ReportView.swift: Sleep report display
  • Info.plist: Required iOS permissions

Sample app: Asleep iOS Sample App

Resources

Official Documentation

Apple Documentation

  • sleeptrack-foundation: Core Asleep concepts, authentication, and data structures
  • sleeptrack-android: Android-specific implementation guide
  • sleeptrack-be: Backend API integration

Next Steps

After integrating the iOS SDK:

  1. Test thoroughly across different iOS devices and versions
  2. Implement proper error handling for all edge cases
  3. Add user-friendly error messages and recovery flows
  4. Consider HealthKit integration for data export
  5. Implement notification reminders for tracking
  6. Add data visualization for sleep trends
  7. Consider Apple Watch companion app
  8. Submit to App Store with proper privacy declarations