Files
tutti/apps/mobile/ios/TuttiMobile/MobileWebAuthenticationSession.swift
T
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

89 lines
2.4 KiB
Swift

import AuthenticationServices
import Foundation
import UIKit
enum MobileWebAuthenticationResult {
case callback
case cancelled
case failed
}
final class MobileWebAuthenticationSession: NSObject,
ASWebAuthenticationPresentationContextProviding
{
private var authenticationSession: ASWebAuthenticationSession?
private weak var presentationAnchor: ASPresentationAnchor?
func start(
url: URL,
callbackScheme: String,
completion: @escaping (MobileWebAuthenticationResult) -> Void
) -> Bool {
guard authenticationSession == nil, let anchor = Self.currentAnchor() else {
return false
}
let completionHandler: ASWebAuthenticationSession.CompletionHandler = {
[weak self] _, error in
self?.authenticationSession = nil
self?.presentationAnchor = nil
guard let error else {
completion(.callback)
return
}
let browserError = error as NSError
let cancelled =
browserError.domain == ASWebAuthenticationSessionError.errorDomain
&& browserError.code
== ASWebAuthenticationSessionError.Code.canceledLogin.rawValue
completion(cancelled ? .cancelled : .failed)
}
let session: ASWebAuthenticationSession
if #available(iOS 17.4, *) {
session = ASWebAuthenticationSession(
url: url,
callback: .customScheme(callbackScheme),
completionHandler: completionHandler
)
} else {
session = ASWebAuthenticationSession(
url: url,
callbackURLScheme: callbackScheme,
completionHandler: completionHandler
)
}
presentationAnchor = anchor
authenticationSession = session
session.presentationContextProvider = self
session.prefersEphemeralWebBrowserSession = false
guard session.start() else {
authenticationSession = nil
presentationAnchor = nil
return false
}
return true
}
func cancel() {
authenticationSession?.cancel()
authenticationSession = nil
presentationAnchor = nil
}
func presentationAnchor(
for _: ASWebAuthenticationSession
) -> ASPresentationAnchor {
presentationAnchor ?? ASPresentationAnchor()
}
private static func currentAnchor() -> ASPresentationAnchor? {
let scenes = UIApplication.shared.connectedScenes.compactMap {
$0 as? UIWindowScene
}
return scenes
.flatMap(\.windows)
.first(where: \.isKeyWindow)
?? scenes.flatMap(\.windows).first
}
}