Skip to content

Building Advanced Modules

Overview

Advanced modules provide sophisticated functionality that goes beyond basic health data management. This guide covers building modules with complex features like machine learning, research capabilities, and enterprise integrations.

Module Architecture

Core Components

Advanced modules typically consist of:

import Spezi

// 1. Module Protocol
protocol AdvancedModule: Module {
    var configuration: AdvancedModuleConfiguration { get }
    var dependencies: [ModuleDependency] { get }
}

// 2. Configuration
struct AdvancedModuleConfiguration {
    let enableML: Bool
    let enableResearch: Bool
    let securityLevel: SecurityLevel
    let dataRetention: TimeInterval
}

// 3. Dependencies
enum ModuleDependency {
    case healthKit
    case user
    case storage
    case networking
}

// 4. Main Module Implementation
@MainActor
final class MyAdvancedModule: AdvancedModule {
    let configuration: AdvancedModuleConfiguration
    let dependencies: [ModuleDependency]

    private var mlEngine: MLEngine?
    private var researchManager: ResearchManager?
    private var securityManager: SecurityManager?

    init(configuration: AdvancedModuleConfiguration) {
        self.configuration = configuration
        self.dependencies = [.healthKit, .user, .storage]
    }

    func configure() async throws {
        // Initialize components based on configuration
        if configuration.enableML {
            mlEngine = try await MLEngine.initialize()
        }

        if configuration.enableResearch {
            researchManager = try await ResearchManager.initialize()
        }

        securityManager = SecurityManager(level: configuration.securityLevel)
    }
}

Machine Learning Integration

Building ML-Enabled Modules

import CoreML
import CreateML

struct MLHealthPredictor {
    private let model: MLModel
    private let featureExtractor: FeatureExtractor

    init(modelURL: URL) throws {
        self.model = try MLModel(contentsOf: modelURL)
        self.featureExtractor = FeatureExtractor()
    }

    func predictHealthRisk(from data: HealthData) async throws -> HealthRiskPrediction {
        let features = try await featureExtractor.extractFeatures(from: data)
        let prediction = try model.prediction(from: features)

        return HealthRiskPrediction(
            riskLevel: prediction.riskLevel,
            confidence: prediction.confidence,
            factors: prediction.contributingFactors
        )
    }
}

class MLEngine {
    private var predictors: [String: MLHealthPredictor] = [:]

    static func initialize() async throws -> MLEngine {
        let engine = MLEngine()

        // Load pre-trained models
        try await engine.loadModel("heart_risk", from: "HeartRiskModel.mlmodel")
        try await engine.loadModel("anomaly_detection", from: "AnomalyModel.mlmodel")

        return engine
    }

    private func loadModel(_ name: String, from filename: String) async throws {
        guard let modelURL = Bundle.main.url(forResource: filename, withExtension: nil) else {
            throw MLError.modelNotFound
        }

        let predictor = try MLHealthPredictor(modelURL: modelURL)
        predictors[name] = predictor
    }

    func predict(_ type: PredictionType, using data: HealthData) async throws -> PredictionResult {
        guard let predictor = predictors[type.rawValue] else {
            throw MLError.predictorNotFound
        }

        return try await predictor.predictHealthRisk(from: data)
    }
}

Real-time ML Processing

class RealTimeMLProcessor {
    private let queue = DispatchQueue(label: "ml.processing", qos: .userInitiated)
    private let mlEngine: MLEngine
    private var processingTask: Task<Void, Never>?

    init(mlEngine: MLEngine) {
        self.mlEngine = mlEngine
    }

    func startProcessing(healthDataStream: AsyncStream<HealthData>) {
        processingTask = Task {
            for await data in healthDataStream {
                await processData(data)
            }
        }
    }

    private func processData(_ data: HealthData) async {
        do {
            let prediction = try await mlEngine.predict(.anomalyDetection, using: data)

            if prediction.isAnomaly {
                await notifyAnomaly(prediction)
            }
        } catch {
            await handleError(error)
        }
    }

    private func notifyAnomaly(_ prediction: PredictionResult) async {
        // Send notification or trigger alert
        NotificationCenter.default.post(
            name: .healthAnomalyDetected,
            object: prediction
        )
    }
}

Research Framework Integration

Building Research Modules

struct ResearchStudy {
    let id: String
    let title: String
    let description: String
    let protocol: StudyProtocol
    let participants: [Participant]
    let dataPoints: [DataPoint]
}

struct StudyProtocol {
    let duration: TimeInterval
    let frequency: DataCollectionFrequency
    let consentRequired: Bool
    let irbApproval: String?
}

class ResearchManager {
    private var activeStudies: [String: ResearchStudy] = [:]
    private let dataCollector: DataCollector
    private let consentManager: ConsentManager

    static func initialize() async throws -> ResearchManager {
        let manager = ResearchManager()
        try await manager.loadActiveStudies()
        return manager
    }

    func createStudy(_ study: ResearchStudy) async throws {
        // Validate study protocol
        try validateProtocol(study.protocol)

        // Set up data collection
        try await dataCollector.setupCollection(for: study)

        // Store study
        activeStudies[study.id] = study
    }

    func enrollParticipant(_ participant: Participant, in studyId: String) async throws {
        guard let study = activeStudies[studyId] else {
            throw ResearchError.studyNotFound
        }

        // Obtain consent if required
        if study.protocol.consentRequired {
            try await consentManager.obtainConsent(from: participant, for: study)
        }

        // Set up participant data collection
        try await dataCollector.enrollParticipant(participant, in: study)
    }

    func collectData(for studyId: String) async throws -> StudyData {
        guard let study = activeStudies[studyId] else {
            throw ResearchError.studyNotFound
        }

        return try await dataCollector.collectData(for: study)
    }
}

Data Collection and Management

class DataCollector {
    private var collectionTasks: [String: Task<Void, Never>] = [:]

    func setupCollection(for study: ResearchStudy) async throws {
        let task = Task {
            await startDataCollection(for: study)
        }
        collectionTasks[study.id] = task
    }

    private func startDataCollection(for study: ResearchStudy) async {
        let timer = Timer.publish(every: study.protocol.frequency.interval, on: .main, in: .common)

        for await _ in timer {
            await collectDataPoint(for: study)
        }
    }

    private func collectDataPoint(for study: ResearchStudy) async {
        for dataPoint in study.dataPoints {
            do {
                let data = try await collectData(for: dataPoint)
                await storeData(data, for: study.id)
            } catch {
                await handleCollectionError(error, for: dataPoint)
            }
        }
    }

    private func collectData(for dataPoint: DataPoint) async throws -> HealthData {
        switch dataPoint.type {
        case .heartRate:
            return try await HealthKitManager.shared.readHeartRate()
        case .stepCount:
            return try await HealthKitManager.shared.readStepCount()
        case .sleepAnalysis:
            return try await HealthKitManager.shared.readSleepAnalysis()
        case .custom:
            return try await collectCustomData(dataPoint)
        }
    }
}

Security and Compliance

Building Secure Modules

enum SecurityLevel {
    case basic
    case enhanced
    case enterprise
}

class SecurityManager {
    private let level: SecurityLevel
    private let encryptionManager: EncryptionManager
    private let auditLogger: AuditLogger
    private let accessController: AccessController

    init(level: SecurityLevel) {
        self.level = level
        self.encryptionManager = EncryptionManager(level: level)
        self.auditLogger = AuditLogger()
        self.accessController = AccessController()
    }

    func encryptData(_ data: Data) async throws -> EncryptedData {
        let encrypted = try await encryptionManager.encrypt(data)
        await auditLogger.log(.dataEncrypted, metadata: ["size": data.count])
        return encrypted
    }

    func decryptData(_ encryptedData: EncryptedData) async throws -> Data {
        let decrypted = try await encryptionManager.decrypt(encryptedData)
        await auditLogger.log(.dataDecrypted, metadata: ["size": decrypted.count])
        return decrypted
    }

    func checkAccess(_ user: User, for resource: Resource) async throws -> Bool {
        let hasAccess = await accessController.checkAccess(user, for: resource)
        await auditLogger.log(.accessChecked, metadata: [
            "user": user.id,
            "resource": resource.id,
            "granted": hasAccess
        ])
        return hasAccess
    }
}

class EncryptionManager {
    private let level: SecurityLevel
    private let keychain: KeychainManager

    init(level: SecurityLevel) {
        self.level = level
        self.keychain = KeychainManager()
    }

    func encrypt(_ data: Data) async throws -> EncryptedData {
        let key = try await keychain.getOrCreateKey(for: level)

        switch level {
        case .basic:
            return try encryptBasic(data, with: key)
        case .enhanced:
            return try encryptEnhanced(data, with: key)
        case .enterprise:
            return try encryptEnterprise(data, with: key)
        }
    }

    private func encryptEnterprise(_ data: Data, with key: SecKey) throws -> EncryptedData {
        // Implement enterprise-grade encryption (AES-256, hardware-backed keys)
        let algorithm = SecKeyAlgorithm.rsaEncryptionOAEPSHA256
        let encryptedData = try SecKeyCreateEncryptedData(key, algorithm, data as CFData, nil)

        return EncryptedData(
            data: encryptedData as Data,
            algorithm: algorithm,
            keyId: key.keyId
        )
    }
}

Performance Optimization

Optimizing Advanced Modules

class PerformanceOptimizer {
    private let cache = NSCache<NSString, AnyObject>()
    private let backgroundQueue = DispatchQueue(label: "background.processing", qos: .utility)

    func optimizeMLProcessing() {
        // Use background processing for ML tasks
        backgroundQueue.async {
            self.processMLTasks()
        }
    }

    func cacheResults<T: AnyObject>(_ results: T, for key: String) {
        cache.setObject(results, forKey: key as NSString)
    }

    func getCachedResults<T: AnyObject>(for key: String) -> T? {
        return cache.object(forKey: key as NSString) as? T
    }

    private func processMLTasks() {
        // Process ML tasks in background
        // This prevents blocking the main thread
    }
}

// Usage in advanced module
class OptimizedAdvancedModule: AdvancedModule {
    private let optimizer = PerformanceOptimizer()

    func processHealthData(_ data: HealthData) async throws -> HealthInsights {
        // Check cache first
        if let cached: HealthInsights = optimizer.getCachedResults(for: data.cacheKey) {
            return cached
        }

        // Process in background if needed
        let insights = try await backgroundProcess(data)

        // Cache results
        optimizer.cacheResults(insights, for: data.cacheKey)

        return insights
    }
}

Testing Advanced Modules

Comprehensive Testing Strategy

import XCTest

class AdvancedModuleTests: XCTestCase {
    var module: MyAdvancedModule!
    var mockMLEngine: MockMLEngine!
    var mockResearchManager: MockResearchManager!

    override func setUp() async throws {
        mockMLEngine = MockMLEngine()
        mockResearchManager = MockResearchManager()

        let config = AdvancedModuleConfiguration(
            enableML: true,
            enableResearch: true,
            securityLevel: .enhanced,
            dataRetention: 3600
        )

        module = MyAdvancedModule(configuration: config)
        module.mlEngine = mockMLEngine
        module.researchManager = mockResearchManager

        try await module.configure()
    }

    func testMLPrediction() async throws {
        // Given
        let healthData = HealthData.mock()
        let expectedPrediction = HealthRiskPrediction.mock()
        mockMLEngine.predictionResult = expectedPrediction

        // When
        let result = try await module.predictHealthRisk(from: healthData)

        // Then
        XCTAssertEqual(result.riskLevel, expectedPrediction.riskLevel)
        XCTAssertEqual(result.confidence, expectedPrediction.confidence)
    }

    func testResearchDataCollection() async throws {
        // Given
        let study = ResearchStudy.mock()
        let participant = Participant.mock()

        // When
        try await module.enrollParticipant(participant, in: study.id)
        let data = try await module.collectStudyData(study.id)

        // Then
        XCTAssertNotNil(data)
        XCTAssertEqual(data.participants.count, 1)
    }

    func testSecurityEncryption() async throws {
        // Given
        let sensitiveData = "sensitive health data".data(using: .utf8)!

        // When
        let encrypted = try await module.encryptData(sensitiveData)
        let decrypted = try await module.decryptData(encrypted)

        // Then
        XCTAssertNotEqual(sensitiveData, encrypted.data)
        XCTAssertEqual(sensitiveData, decrypted)
    }
}

Best Practices

1. Modular Design

  • Keep modules focused on specific functionality
  • Use dependency injection for flexibility
  • Implement clear interfaces between components

2. Error Handling

  • Provide meaningful error messages
  • Implement graceful degradation
  • Log errors for debugging

3. Performance

  • Use background processing for heavy tasks
  • Implement caching strategies
  • Optimize data structures and algorithms

4. Security

  • Follow security best practices
  • Implement proper encryption
  • Maintain audit trails

5. Testing

  • Write comprehensive unit tests
  • Test edge cases and error conditions
  • Use mocking for external dependencies

Next Steps