# 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 `/flags/poll` | | `BLSyncEngine` | Generic offline-first sync engine with delta pull + batch push | ## 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 | Status | | ---------- | ----------------------------------- | ---------------------------------------------------- | | ChronoMind | `learning_ai_clock` | Migration from local Cloud/ files | | LysnrAI | `learning_voice_ai_agent` | Migration from local Util/ + Auth/ files | | PeakPulse | `learning_ai_peakpulse` | New — will use SDK from day one | | NomGap | `learning_ai_fastgap` | Future — React Native, will use TS packages directly | | MindLyst | `learning_multimodal_memory_agents` | Future — KMP, may need Kotlin equivalent | ## What This Replaces Before this SDK, each iOS app had its own copy of platform integration code: | ChronoMind (old) | LysnrAI (old) | SDK (new) | | --------------------------------- | ------------------------------ | -------------------------------- | | `CMTelemetryService` (139 lines) | `TelemetryService` (288 lines) | `BLTelemetryClient` | | `CMAuthService` (359 lines) | `AuthService` (exists) | `BLAuthClient` | | `KeychainHelper` (53 lines) | `KeychainHelper` (exists) | `BLKeychain` | | `FeatureFlagService` (72 lines) | `FeatureFlagService` (exists) | `BLFeatureFlagClient` | | `PlatformSyncManager` (450 lines) | Various sync files | `BLSyncEngine` + product adapter | Total duplicated code eliminated: **~1,100+ lines per product app**. ## 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+