chore(ios): 添加LoginView并修复TokenStore类型与线程调度问题

- 在Xcode项目中添加LoginView.swift文件引用
- 修复TokenStore中Keychain查询字典类型为CFString
- 将DispatchQueue.main.async替换为MainActor.run,避免弱引用丢失
- 修复绑定设备信息时的线程安全与变量命名
This commit is contained in:
2026-08-03 20:51:16 +08:00
parent 5e369f14d9
commit da0a3bf59d
3 changed files with 18 additions and 11 deletions

View File

@@ -28,6 +28,7 @@
FB0000000000000000000012 /* VideoRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0000000000000000000012 /* VideoRecorder.swift */; }; FB0000000000000000000012 /* VideoRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0000000000000000000012 /* VideoRecorder.swift */; };
FB0000000000000000000013 /* SetupPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0000000000000000000013 /* SetupPanelView.swift */; }; FB0000000000000000000013 /* SetupPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0000000000000000000013 /* SetupPanelView.swift */; };
FB0000000000000000000014 /* ControlPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0000000000000000000014 /* ControlPanelView.swift */; }; FB0000000000000000000014 /* ControlPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0000000000000000000014 /* ControlPanelView.swift */; };
FB0000000000000000000017 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0000000000000000000017 /* LoginView.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@@ -53,6 +54,7 @@
FA0000000000000000000012 /* VideoRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRecorder.swift; sourceTree = "<group>"; }; FA0000000000000000000012 /* VideoRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRecorder.swift; sourceTree = "<group>"; };
FA0000000000000000000013 /* SetupPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupPanelView.swift; sourceTree = "<group>"; }; FA0000000000000000000013 /* SetupPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupPanelView.swift; sourceTree = "<group>"; };
FA0000000000000000000014 /* ControlPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlPanelView.swift; sourceTree = "<group>"; }; FA0000000000000000000014 /* ControlPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlPanelView.swift; sourceTree = "<group>"; };
FA0000000000000000000017 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -145,6 +147,7 @@
FA0000000000000000000008 /* RemoteTouchView.swift */, FA0000000000000000000008 /* RemoteTouchView.swift */,
FA0000000000000000000009 /* RemoteVideoView.swift */, FA0000000000000000000009 /* RemoteVideoView.swift */,
FA000000000000000000000A /* SelfCodecDisplayView.swift */, FA000000000000000000000A /* SelfCodecDisplayView.swift */,
FA0000000000000000000017 /* LoginView.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -256,6 +259,7 @@
FB0000000000000000000008 /* RemoteTouchView.swift in Sources */, FB0000000000000000000008 /* RemoteTouchView.swift in Sources */,
FB0000000000000000000009 /* RemoteVideoView.swift in Sources */, FB0000000000000000000009 /* RemoteVideoView.swift in Sources */,
FB000000000000000000000A /* SelfCodecDisplayView.swift in Sources */, FB000000000000000000000A /* SelfCodecDisplayView.swift in Sources */,
FB0000000000000000000017 /* LoginView.swift in Sources */,
FB000000000000000000000B /* ContentView.swift in Sources */, FB000000000000000000000B /* ContentView.swift in Sources */,
FB000000000000000000000C /* AuthSheetView.swift in Sources */, FB000000000000000000000C /* AuthSheetView.swift in Sources */,
FB000000000000000000000D /* ControllerViewModel.swift in Sources */, FB000000000000000000000D /* ControllerViewModel.swift in Sources */,

View File

@@ -42,7 +42,7 @@ enum TokenStore {
static func clear() { static func clear() {
for k in [accessKey, refreshKey, userKey] { for k in [accessKey, refreshKey, userKey] {
let query: [String: Any] = [ let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword, kSecClass: kSecClassGenericPassword,
kSecAttrService: service, kSecAttrService: service,
kSecAttrAccount: k kSecAttrAccount: k
@@ -55,7 +55,7 @@ enum TokenStore {
private static func save(key: String, value: String) { private static func save(key: String, value: String) {
// //
let query: [String: Any] = [ let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword, kSecClass: kSecClassGenericPassword,
kSecAttrService: service, kSecAttrService: service,
kSecAttrAccount: key, kSecAttrAccount: key,
@@ -66,7 +66,7 @@ enum TokenStore {
} }
private static func load(key: String) -> String? { private static func load(key: String) -> String? {
let query: [String: Any] = [ let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword, kSecClass: kSecClassGenericPassword,
kSecAttrService: service, kSecAttrService: service,
kSecAttrAccount: key, kSecAttrAccount: key,

View File

@@ -391,7 +391,8 @@ final class ControllerViewModel: NSObject, ObservableObject {
do { do {
let newAt = try await ensureAccessToken() let newAt = try await ensureAccessToken()
accessToken = newAt accessToken = newAt
DispatchQueue.main.async { await MainActor.run { [weak self] in
guard let self else { return }
self.signalingClient?.disconnect() self.signalingClient?.disconnect()
self.signalingClient = nil self.signalingClient = nil
// REGISTER_SUCCESS startWebRTC // REGISTER_SUCCESS startWebRTC
@@ -403,7 +404,8 @@ final class ControllerViewModel: NSObject, ObservableObject {
signaling.connect() signaling.connect()
} }
} catch { } catch {
DispatchQueue.main.async { await MainActor.run { [weak self] in
guard let self else { return }
self.logout() self.logout()
self.alertMessage = "令牌刷新失败,请重新登录" self.alertMessage = "令牌刷新失败,请重新登录"
} }
@@ -417,13 +419,14 @@ final class ControllerViewModel: NSObject, ObservableObject {
do { do {
let data = try await ApiClient.bindings(accessToken: at) let data = try await ApiClient.bindings(accessToken: at)
guard let list = data["bindings"] as? [[String: Any]], !list.isEmpty else { return } guard let list = data["bindings"] as? [[String: Any]], !list.isEmpty else { return }
let uids = list.compactMap { $ -> String? in let uids = list.compactMap { entry -> String? in
if let u = $["deviceUid"] as? String, !u.isEmpty { return u } if let u = entry["deviceUid"] as? String, !u.isEmpty { return u }
return $["deviceId"] as? String return entry["deviceId"] as? String
}.filter { !$0.isEmpty } }.filter { !$0.isEmpty }
if !uids.isEmpty { if !uids.isEmpty {
DispatchQueue.main.async { let message = "已绑定设备: " + uids.joined(separator: ", ")
self.alertMessage = "已绑定设备: " + uids.joined(separator: ", ") Task { @MainActor [weak self] in
self?.alertMessage = message
} }
} }
} catch { /* */ } } catch { /* */ }
@@ -470,7 +473,7 @@ final class ControllerViewModel: NSObject, ObservableObject {
webRTCClient?.stats { [weak self] report in webRTCClient?.stats { [weak self] report in
guard let self else { return } guard let self else { return }
let text = self.buildStatsText(report) let text = self.buildStatsText(report)
DispatchQueue.main.async { Task { @MainActor in
self.statsText = text self.statsText = text
} }
} }