84 lines
3.2 KiB
Swift
84 lines
3.2 KiB
Swift
// SPDX-FileCopyrightText: 2026 Kaede Akino
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
import Foundation
|
|
import UIKit
|
|
|
|
/// Links keep their existing workspace UUID. Window ownership is resolved locally by the app.
|
|
@MainActor
|
|
enum MobileWorkspaceSceneRouting {
|
|
nonisolated enum CloseError: Error {
|
|
case sessionNotFound
|
|
}
|
|
|
|
static let activityType = "com.akinokaede.termind.open-workspace"
|
|
static let closedWindowActivityType = "com.akinokaede.termind.reopen-window"
|
|
private static let workspaceIDKey = "workspaceID"
|
|
|
|
static func openSessionIDs() -> Set<String> {
|
|
Set(
|
|
UIApplication.shared.openSessions
|
|
.filter { $0.role == .windowApplication }
|
|
.map(\.persistentIdentifier))
|
|
}
|
|
|
|
static func activity(for workspaceID: UUID) -> NSUserActivity {
|
|
let activity = NSUserActivity(activityType: activityType)
|
|
activity.userInfo = [workspaceIDKey: workspaceID.uuidString]
|
|
return activity
|
|
}
|
|
|
|
static func workspaceID(from activity: NSUserActivity) -> UUID? {
|
|
guard activity.activityType == activityType,
|
|
let value = activity.userInfo?[workspaceIDKey] as? String
|
|
else { return nil }
|
|
return UUID(uuidString: value)
|
|
}
|
|
|
|
static func createWindow(_ recordID: UUID, failed: @escaping @MainActor () -> Void) {
|
|
let activity = NSUserActivity(activityType: closedWindowActivityType)
|
|
activity.userInfo = ["closedRecordID": recordID.uuidString]
|
|
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil) { _ in
|
|
MainActor.assumeIsolated { failed() }
|
|
}
|
|
}
|
|
|
|
static func closedRecordID(from activity: NSUserActivity) -> UUID? {
|
|
guard activity.activityType == closedWindowActivityType,
|
|
let value = activity.userInfo?["closedRecordID"] as? String
|
|
else { return nil }
|
|
return UUID(uuidString: value)
|
|
}
|
|
|
|
static func reconcile(app: MobileAppModel, activeSessionID: String?) {
|
|
app.reconcileWorkspaceSessions(
|
|
openSessionIDs: openSessionIDs(),
|
|
activeSessionID: activeSessionID,
|
|
supportsMultipleScenes: UIApplication.shared.supportsMultipleScenes)
|
|
}
|
|
|
|
static func activate(sessionID: String, workspaceID: UUID, unavailable: @escaping @MainActor () -> Void) {
|
|
guard let session = UIApplication.shared.openSessions.first(where: { $0.persistentIdentifier == sessionID })
|
|
else {
|
|
unavailable()
|
|
return
|
|
}
|
|
UIApplication.shared.requestSceneSessionActivation(
|
|
session, userActivity: activity(for: workspaceID), options: nil
|
|
) { _ in
|
|
MainActor.assumeIsolated { unavailable() }
|
|
}
|
|
}
|
|
|
|
static func close(sessionID: String, failed: @escaping @MainActor (any Error) -> Void) {
|
|
guard let session = UIApplication.shared.openSessions.first(where: { $0.persistentIdentifier == sessionID })
|
|
else {
|
|
failed(CloseError.sessionNotFound)
|
|
return
|
|
}
|
|
UIApplication.shared.requestSceneSessionDestruction(session, options: nil) { error in
|
|
MainActor.assumeIsolated { failed(error) }
|
|
}
|
|
}
|
|
}
|