Extracts duplicated platform integration code from ChronoMind + LysnrAI into a single Swift Package. Eliminates ~1,100+ lines of copied code per product app. Components: - BLPlatformConfig — product-specific configuration (productId, baseURL, bundleId) - BLPlatformClient — generic HTTP client with auth injection, x-request-id, timeout - BLKeychain — Keychain CRUD for secure token storage - BLTelemetryClient — telemetry queue + batch flush (matches @bytelyst/telemetry-client) - BLAuthClient — full auth operations (matches @bytelyst/auth-client) - BLFeatureFlagClient — feature flag polling from platform-service /flags/poll - BLSyncEngine — generic offline-first sync with delta pull + batch push Platforms: iOS 17+, watchOS 10+, macOS 14+
157 lines
6.5 KiB
Markdown
157 lines
6.5 KiB
Markdown
# 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+
|