# ByteLystPlatformSDK Shared Swift platform client for all ByteLyst iOS/watchOS/macOS apps. Eliminates code duplication across products by providing a single source of truth for platform-service integration. ## What's Inside | File | What It Does | | --------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `BLPlatformConfig` | Product-specific configuration (productId, baseURL, bundleId, appGroupId) | | `BLPlatformClient` | Generic HTTP client with auth injection, x-request-id, timeout | | `BLKeychain` | Keychain CRUD for secure token storage | | `BLTelemetryClient` | Telemetry event queue + batch flush (matches `@bytelyst/telemetry-client`) | | `BLAuthClient` | Auth operations: login, register, refresh, password ops, email verify, delete account (matches `@bytelyst/auth-client`) | | `BLFeatureFlagClient` | Feature flag polling from platform-service `/api/flags/poll` | | `BLSyncEngine` | Generic offline-first sync engine with delta pull + batch push | | `BLBlobClient` | Azure Blob Storage upload via SAS tokens from platform-service | | `BLKillSwitchClient` | Kill switch check from platform-service (fail-open) | | `BLLicenseClient` | License key activation + status via platform-service | | `BLBiometricAuth` | Face ID / Touch ID wrapper (LocalAuthentication) | | `BLCrashReporter` | MetricKit crash and hang reporting with local storage | | `BLAuditLogger` | Local rotating JSON audit log for debugging | ## Usage ### 1. Add to Xcode Project In Xcode: **File → Add Package Dependencies → Add Local...** → select this directory: ``` ../learning_ai_common_plat/packages/swift-platform-sdk/ ``` Or in `Package.swift`: ```swift .package(path: "../learning_ai_common_plat/packages/swift-platform-sdk") ``` ### 2. Configure at App Launch ```swift import ByteLystPlatformSDK // Create config — one per app let config = BLPlatformConfig.fromInfoPlist( productId: "peakpulse", defaultBaseURL: "https://api.peakpulse.app", bundleId: "com.saravana.peakpulse" ) // Create shared HTTP client let client = BLPlatformClient(config: config) // Create services let telemetry = BLTelemetryClient(config: config, client: client) let auth = BLAuthClient(config: config, client: client) let flags = BLFeatureFlagClient(config: config, client: client) ``` ### 3. Telemetry ```swift telemetry.start() telemetry.trackEvent("info", module: "session", name: "session_started", metrics: ["elevation": 2450.0]) telemetry.trackScreen("live_tracking") // On app background: telemetry.stop() ``` ### 4. Auth ```swift // Login let user = try await auth.login(email: "user@example.com", password: "secret") // Restore on launch await auth.restoreSession() // Listen for state changes auth.onAuthStateChanged = { state in switch state { case .loggedIn(let user): print("Hello, \(user.displayName)") case .loggedOut: print("Signed out") case .error(let msg): print("Auth error: \(msg)") case .loading: print("Loading...") } } ``` ### 5. Feature Flags ```swift flags.start(userId: auth.accessToken != nil ? "user-id" : nil) if flags.isEnabled("peakpulse.pro_charts") { // Show Pro charts } ``` ### 6. Sync Engine ```swift // Implement your product-specific adapter struct PeakSessionSyncAdapter: BLSyncAdapter { typealias SyncItem = PeakSessionDTO func pullDelta(since: Date?, client: BLPlatformClient) async throws -> [PeakSessionDTO] { var path = "/api/peak-sessions/sync" if let since { path += "?since=\(ISO8601DateFormatter().string(from: since))" } return try await client.request(path: path, responseType: [PeakSessionDTO].self) } func pushBatch(_ items: [BLOfflineQueueItem], client: BLPlatformClient) async throws -> BLBatchResult { let body = try JSONEncoder().encode(["items": items.compactMap(\.payload)]) let (data, _) = try await client.rawRequest(path: "/api/peak-sessions/batch", method: "POST", body: body) return try JSONDecoder().decode(BLBatchResult.self, from: data) } } let syncEngine = BLSyncEngine( config: config, client: client, adapter: PeakSessionSyncAdapter() ) ``` ## Product Apps Using This SDK | Product | Repo | Wrappers | Status | | ---------- | ----------------------------------- | -------- | ----------------------------------- | | ChronoMind | `learning_ai_clock` | 5 files | ✅ Migrated (Cloud/ + Diagnostics/) | | LysnrAI | `learning_voice_ai_agent` | 9 files | ✅ Migrated (Auth/ + Util/) | | MindLyst | `learning_multimodal_memory_agents` | 4 files | ✅ Migrated (Services/) | | PeakPulse | `learning_ai_peakpulse` | — | New — will use SDK from day one | | NomGap | `learning_ai_fastgap` | — | React Native — uses TS packages | ## What This Replaces Before this SDK, each iOS app had its own copy of platform integration code: | ChronoMind (old) | LysnrAI (old) | MindLyst (old) | SDK (new) | | --------------------------------- | ------------------------------- | ------------------------------- | --------------------- | | `KeychainHelper` (53 lines) | `KeychainHelper` (60 lines) | `KeychainHelper` (60 lines) | `BLKeychain` | | `CMTelemetryService` (139 lines) | `TelemetryService` (288 lines) | `TelemetryService` (139 lines) | `BLTelemetryClient` | | `CMAuthService` (359 lines) | `AuthService` (421 lines) | `AuthService` (389 lines) | `BLAuthClient` | | `FeatureFlagService` (72 lines) | `FeatureFlagService` (71 lines) | `FeatureFlagService` (72 lines) | `BLFeatureFlagClient` | | `CrashReporter` (153 lines) | — | — | `BLCrashReporter` | | — | `BlobService` (118 lines) | — | `BLBlobClient` | | — | `KillSwitchService` (48 lines) | — | `BLKillSwitchClient` | | — | `LicenseService` (135 lines) | — | `BLLicenseClient` | | — | `BiometricAuth` (65 lines) | — | `BLBiometricAuth` | | — | `AuditLogger` (70 lines) | — | `BLAuditLogger` | | `PlatformSyncManager` (450 lines) | Various sync files | — | `BLSyncEngine` | Total duplicated code eliminated: **~2,600+ lines across 3 product apps**. ## Design Decisions 1. **No `@MainActor`** — the SDK is thread-safe via NSLock. Product apps can wrap in `@MainActor` at the view model layer. 2. **No singletons** — product apps own the lifecycle. Create instances at app launch, inject where needed. 3. **No SwiftUI dependency** — pure Foundation. Works in watchOS, macOS, widgets, extensions. 4. **Protocol-based sync** — `BLSyncAdapter` lets each product define its own DTOs and endpoints while reusing the queue/timer/conflict plumbing. 5. **Fire-and-forget telemetry** — errors never surface to the user. Matches the TypeScript package behavior. ## Platforms - iOS 17+ - watchOS 10+ - macOS 14+ ## Broadcast & Survey New in v1.2: In-app messaging and survey capabilities. ### Broadcast Client ```swift import ByteLystPlatformSDK let config = BLPlatformConfig( productId: "lysnrai", baseURL: URL(string: "https://api.bytelyst.io/v1")!, getAuthToken: { await getToken() } ) let broadcastClient = BLBroadcastClient(config: config) // Start polling for messages broadcastClient.startPolling(intervalMs: 60000) { messages in // Handle new messages } // SwiftUI UI components BLInAppMessageBanner(client: broadcastClient, position: .top) BLBroadcastModal(client: broadcastClient) ``` ### Survey Client ```swift let surveyClient = BLSurveyClient(config: config) // Check for active surveys let (survey, _) = await surveyClient.getActiveSurvey() // Start and complete survey await surveyClient.startSurvey(surveyId: survey.id) await surveyClient.submitAnswer(surveyId: survey.id, questionId: "q1", answer: answer) await surveyClient.completeSurvey(surveyId: survey.id) // SwiftUI modal BLSurveyModal(client: surveyClient) ``` See [Broadcast & Survey Guide](BROADCAST_SURVEY_GUIDE.md) for full documentation.