Files
duooomi-ios-sdk/DuooomiTest/Sources/SDKManager.swift
2026-04-08 15:41:46 +08:00

230 lines
7.1 KiB
Swift

import UIKit
import Combine
import DuooomiBleSDK
struct DiscoveredDevice: Identifiable {
let id: String
let name: String
let rssi: Int
}
struct DeviceInfo {
let brand: String
let screenSize: String
let freeSpace: Int
let totalSpace: Int
let batteryLevel: Int
}
struct LogEntry: Identifiable {
let id = UUID()
let time: Date
let message: String
}
@MainActor
class SDKManager: ObservableObject {
static let shared = SDKManager()
// State
@Published var btState = "idle"
@Published var connectedDevice: [String: Any]? = nil
@Published var deviceInfo: DeviceInfo? = nil
@Published var version = ""
@Published var isActivated = false
@Published var transferProgress: Int = 0
@Published var error: String? = nil
@Published var discoveredDevices: [DiscoveredDevice] = []
@Published var logs: [LogEntry] = []
@Published var isSDKReady = false
private var cancellables = Set<AnyCancellable>()
private var listenerIds: [String] = []
private init() {}
func initialize() {
ReactNativeHostManager.shared.initialize()
// RN JS
do {
let view = try ReactNativeHostManager.shared.loadView(
moduleName: "main", initialProps: nil, launchOptions: nil)
//
_rnView = view
log("SDK initialized, RN runtime started")
isSDKReady = true
} catch {
log("SDK init failed: \(error.localizedDescription)")
}
setupSubscriptions()
setupMessageListener()
}
private var _rnView: UIView?
// MARK: - State Subscriptions
private func setupSubscriptions() {
BrownfieldState.subscribe("btState") { [weak self] value in
DispatchQueue.main.async {
let state = value as? String ?? "idle"
self?.btState = state
self?.log("btState → \(state)")
}
}.store(in: &cancellables)
BrownfieldState.subscribe("connectedDevice") { [weak self] value in
DispatchQueue.main.async {
self?.connectedDevice = value as? [String: Any]
if let device = value as? [String: Any] {
self?.log("connectedDevice → \(device["name"] ?? "unknown")")
} else {
self?.log("connectedDevice → nil")
}
}
}.store(in: &cancellables)
BrownfieldState.subscribe("deviceInfo") { [weak self] value in
DispatchQueue.main.async {
if let dict = value as? [String: Any] {
self?.deviceInfo = DeviceInfo(
brand: dict["brand"] as? String ?? "",
screenSize: dict["screenSize"] as? String ?? "",
freeSpace: dict["freeSpace"] as? Int ?? 0,
totalSpace: dict["totalSpace"] as? Int ?? 0,
batteryLevel: dict["batteryLevel"] as? Int ?? 0
)
self?.log("deviceInfo updated")
} else {
self?.deviceInfo = nil
}
}
}.store(in: &cancellables)
BrownfieldState.subscribe("version") { [weak self] value in
DispatchQueue.main.async {
self?.version = value as? String ?? ""
if let v = value as? String {
self?.log("version → \(v)")
}
}
}.store(in: &cancellables)
BrownfieldState.subscribe("isActivated") { [weak self] value in
DispatchQueue.main.async {
self?.isActivated = value as? Bool ?? false
}
}.store(in: &cancellables)
BrownfieldState.subscribe("transferProgress") { [weak self] value in
DispatchQueue.main.async {
self?.transferProgress = value as? Int ?? 0
}
}.store(in: &cancellables)
BrownfieldState.subscribe("error") { [weak self] value in
DispatchQueue.main.async {
self?.error = value as? String
if let e = value as? String {
self?.log("ERROR: \(e)")
}
}
}.store(in: &cancellables)
BrownfieldState.subscribe("discoveredDevices") { [weak self] value in
DispatchQueue.main.async {
if let arr = value as? [[String: Any]] {
self?.discoveredDevices = arr.compactMap { dict in
guard let id = dict["id"] as? String else { return nil }
return DiscoveredDevice(
id: id,
name: dict["name"] as? String ?? "Unknown",
rssi: dict["rssi"] as? Int ?? 0
)
}
} else {
self?.discoveredDevices = []
}
}
}.store(in: &cancellables)
}
// MARK: - Message Listener
private func setupMessageListener() {
let id = BrownfieldMessaging.addListener { [weak self] message in
DispatchQueue.main.async {
let event = message["event"] as? String ?? "unknown"
self?.log("Event: \(event)\(message)")
}
}
listenerIds.append(id)
}
// MARK: - Actions
func scan() {
log("→ scan")
BrownfieldMessaging.sendMessage(["action": "scan"])
}
func stopScan() {
log("→ stopScan")
BrownfieldMessaging.sendMessage(["action": "stopScan"])
}
func connect(deviceId: String) {
log("→ connect \(deviceId)")
BrownfieldMessaging.sendMessage(["action": "connect", "deviceId": deviceId])
}
func disconnect() {
log("→ disconnect")
BrownfieldMessaging.sendMessage(["action": "disconnect"])
}
func bind(userId: String) {
log("→ bind \(userId)")
BrownfieldMessaging.sendMessage(["action": "bind", "userId": userId])
}
func unbind(userId: String) {
log("→ unbind \(userId)")
BrownfieldMessaging.sendMessage(["action": "unbind", "userId": userId])
}
func getDeviceInfo() {
log("→ getDeviceInfo")
BrownfieldMessaging.sendMessage(["action": "getDeviceInfo"])
}
func getVersion() {
log("→ getVersion")
BrownfieldMessaging.sendMessage(["action": "getVersion"])
}
func deleteFile(key: String) {
log("→ deleteFile \(key)")
BrownfieldMessaging.sendMessage(["action": "deleteFile", "key": key])
}
func transferFile(fileUri: String, commandType: Int) {
log("→ transferFile \(fileUri)")
BrownfieldMessaging.sendMessage([
"action": "transferFile",
"fileUri": fileUri,
"commandType": commandType
])
}
// MARK: - Logging
private func log(_ message: String) {
let entry = LogEntry(time: Date(), message: message)
logs.insert(entry, at: 0)
if logs.count > 200 { logs.removeLast() }
}
}