feat(ios): reconnect to last bridge
This commit is contained in:
94
apps/ios/Sources/Bridge/BridgeConnectionController.swift
Normal file
94
apps/ios/Sources/Bridge/BridgeConnectionController.swift
Normal file
@@ -0,0 +1,94 @@
|
||||
import ClawdisKit
|
||||
import Combine
|
||||
import Foundation
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
final class BridgeConnectionController: ObservableObject {
|
||||
@Published private(set) var bridges: [BridgeDiscoveryModel.DiscoveredBridge] = []
|
||||
@Published private(set) var discoveryStatusText: String = "Idle"
|
||||
|
||||
private let discovery = BridgeDiscoveryModel()
|
||||
private weak var appModel: NodeAppModel?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var didAutoConnect = false
|
||||
|
||||
init(appModel: NodeAppModel) {
|
||||
self.appModel = appModel
|
||||
|
||||
BridgeSettingsStore.bootstrapPersistence()
|
||||
|
||||
self.discovery.$bridges
|
||||
.sink { [weak self] newValue in
|
||||
guard let self else { return }
|
||||
self.bridges = newValue
|
||||
self.maybeAutoConnect()
|
||||
}
|
||||
.store(in: &self.cancellables)
|
||||
|
||||
self.discovery.$statusText
|
||||
.assign(to: &self.$discoveryStatusText)
|
||||
|
||||
self.discovery.start()
|
||||
}
|
||||
|
||||
func setScenePhase(_ phase: ScenePhase) {
|
||||
switch phase {
|
||||
case .background:
|
||||
self.discovery.stop()
|
||||
case .active, .inactive:
|
||||
self.discovery.start()
|
||||
@unknown default:
|
||||
self.discovery.start()
|
||||
}
|
||||
}
|
||||
|
||||
private func maybeAutoConnect() {
|
||||
guard !self.didAutoConnect else { return }
|
||||
guard let appModel = self.appModel else { return }
|
||||
guard appModel.bridgeServerName == nil else { return }
|
||||
|
||||
let defaults = UserDefaults.standard
|
||||
let preferredStableID = defaults.string(forKey: "bridge.preferredStableID")?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
guard !preferredStableID.isEmpty else { return }
|
||||
|
||||
let instanceId = defaults.string(forKey: "node.instanceId")?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
guard !instanceId.isEmpty else { return }
|
||||
|
||||
let token = KeychainStore.loadString(
|
||||
service: "com.steipete.clawdis.bridge",
|
||||
account: "bridge-token.\(instanceId)")?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
guard !token.isEmpty else { return }
|
||||
|
||||
guard let target = self.bridges.first(where: { $0.stableID == preferredStableID }) else { return }
|
||||
|
||||
self.didAutoConnect = true
|
||||
appModel.connectToBridge(endpoint: target.endpoint, hello: self.makeHello(token: token))
|
||||
}
|
||||
|
||||
private func makeHello(token: String) -> BridgeHello {
|
||||
let defaults = UserDefaults.standard
|
||||
let nodeId = defaults.string(forKey: "node.instanceId") ?? "ios-node"
|
||||
let displayName = defaults.string(forKey: "node.displayName") ?? "iOS Node"
|
||||
|
||||
return BridgeHello(
|
||||
nodeId: nodeId,
|
||||
displayName: displayName,
|
||||
token: token,
|
||||
platform: self.platformString(),
|
||||
version: self.appVersion())
|
||||
}
|
||||
|
||||
private func platformString() -> String {
|
||||
let v = ProcessInfo.processInfo.operatingSystemVersion
|
||||
return "iOS \(v.majorVersion).\(v.minorVersion).\(v.patchVersion)"
|
||||
}
|
||||
|
||||
private func appVersion() -> String {
|
||||
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "dev"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user