# 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.

