// ── ByteLystPlatform ──────────────────────────────────────── // Unified entry point for the ByteLyst platform SDK. // Creates and wires all platform services from a single config. // // Usage: // let platform = ByteLystPlatform(config: .init( // productId: "peakpulse", // baseURL: "https://api.peakpulse.app", // bundleId: "com.saravana.peakpulse" // )) // // platform.start() // Start telemetry + flags + kill switch // platform.telemetry.trackScreen("home") // Track events // let isNew = platform.flags.isEnabled("new_feature") // platform.stop() // Flush + stop timers import Foundation /// Unified entry point that wires all ByteLyst platform services together. /// Create one instance at app launch and access services via properties. public final class ByteLystPlatform { /// Platform configuration. public let config: BLPlatformConfig /// HTTP client shared by all services. public let client: BLPlatformClient /// Telemetry event tracking. public let telemetry: BLTelemetryClient /// Feature flag polling. public let flags: BLFeatureFlagClient /// Kill switch checker. public let killSwitch: BLKillSwitchClient /// Crash reporter (MetricKit). Created lazily on MainActor. public private(set) var crashReporter: BLCrashReporter? /// Keychain access (via bundleId as service). public let keychain: BLKeychainAccessor /// Audit logger type (static API — call BLAuditLogger.log()). public let auditLog: BLAuditLogger.Type = BLAuditLogger.self /// Auth client. public let auth: BLAuthClient /// Whether `start()` has been called. public private(set) var isStarted = false public init(config: BLPlatformConfig) { self.config = config self.client = BLPlatformClient(config: config) self.telemetry = BLTelemetryClient(config: config, client: client) self.flags = BLFeatureFlagClient(config: config, client: client) self.killSwitch = BLKillSwitchClient(config: config) self.keychain = BLKeychainAccessor(service: config.bundleId) self.auth = BLAuthClient(config: config, client: client) BLAuditLogger.configure(fileName: "\(config.productId)_audit_log.json") } /// Test-only initializer that accepts a custom URLSessionConfiguration. public init(config: BLPlatformConfig, sessionConfiguration: URLSessionConfiguration) { self.config = config self.client = BLPlatformClient(config: config, sessionConfiguration: sessionConfiguration) self.telemetry = BLTelemetryClient(config: config, client: client) self.flags = BLFeatureFlagClient(config: config, client: client) self.killSwitch = BLKillSwitchClient(config: config) self.keychain = BLKeychainAccessor(service: config.bundleId) self.auth = BLAuthClient(config: config, client: client) BLAuditLogger.configure(fileName: "\(config.productId)_audit_log.json") } // MARK: - Lifecycle /// Start all services: telemetry flush timer, feature flag polling, kill switch check. public func start(userId: String? = nil) { guard !isStarted else { return } isStarted = true telemetry.start() flags.start(userId: userId) Task { await killSwitch.check() } Task { @MainActor in self.crashReporter = BLCrashReporter(productId: config.productId) } } /// Stop all services: flush telemetry, stop flag polling. public func stop() { guard isStarted else { return } isStarted = false telemetry.stop() flags.stop() } } // MARK: - Keychain Accessor /// Convenience wrapper around BLKeychain that binds to a specific service (bundleId). public struct BLKeychainAccessor { private let service: String public init(service: String) { self.service = service } @discardableResult public func save(key: String, value: String) -> Bool { BLKeychain.save(service: service, key: key, value: value) } public func read(key: String) -> String? { BLKeychain.read(service: service, key: key) } @discardableResult public func delete(key: String) -> Bool { BLKeychain.delete(service: service, key: key) } }