learning_ai_common_plat/packages/swift-platform-sdk
saravanakumardb1 2c330387fc feat(auth): native SDK passkey + BLAuthUI Swift + Kotlin social/MFA
SmartAuth v2 SDK extensions for both Swift and Kotlin platform SDKs:

Swift (BLAuthClient.swift):
- Social login, MFA, passkeys, providers, devices, step-up, login history
- New types: BLMfaChallenge, BLTotpSetup, BLMfaStatus, BLAuthProvider, etc.
- BLAuthState: added .mfaRequired case

Swift (BLAuthUI.swift) — 4 reusable views:
- BLLoginView, BLMfaChallengeView, BLPasskeyView, BLStepUpSheet

Kotlin (BLAuthClient.kt):
- Social login, MFA, providers, devices, step-up, login history
- MFA challenge detection in login(), encodeMap() helper

Kotlin (BLPasskeyManager.kt) — Credential Manager passkey wrapper
Kotlin (BLAuthUI.kt) — 5 Compose screens matching Swift BLAuthUI
Kotlin build.gradle.kts — Credential Manager dependencies

Tests: Swift (6 methods), Kotlin (5 methods)
2026-03-12 10:55:32 -07:00
..
Sources feat(auth): native SDK passkey + BLAuthUI Swift + Kotlin social/MFA 2026-03-12 10:55:32 -07:00
Tests feat(auth): native SDK passkey + BLAuthUI Swift + Kotlin social/MFA 2026-03-12 10:55:32 -07:00
Package.swift feat(swift-sdk): add ByteLystPlatformSDK — shared Swift package for all iOS/watchOS/macOS apps 2026-02-28 22:12:20 -08:00
README.md docs(swift,kotlin): Add comprehensive SDK READMEs with broadcast and survey examples 2026-03-03 08:30:26 -08:00

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:

.package(path: "../learning_ai_common_plat/packages/swift-platform-sdk")

2. Configure at App Launch

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

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

// 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

flags.start(userId: auth.accessToken != nil ? "user-id" : nil)
if flags.isEnabled("peakpulse.pro_charts") {
    // Show Pro charts
}

6. Sync Engine

// 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 syncBLSyncAdapter 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

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

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 for full documentation.