Swift中如何等待OAuth回调完成后再处理回调URL?
Swift OAuth授权流程时序问题:Token未完成存储就触发页面刷新
触发runOauth方法后,AppDelegate的application(_:open:options:)会先于callBackOAuth回调执行,导致授权完成后viewDidAppear重新调用时,keychain中的Token尚未被设置。
相关代码实现
1. OAuth授权触发与回调方法
func runOauth(){ let appDelegate = UIApplication.shared.delegate as! AppDelegate appDelegate.oauth2!.afterAuthorizeOrFail = self.callBackOAuth var url:URL? do{ url = try appDelegate.oauth2!.authorizeURL(withRedirect:"kronos://oauth/callback", scope: "auth",params: ["tg":"addon/kronos/main","idx":"login.OAuth","formId":"iOS"]) do{ let authorizer = appDelegate.oauth2!.authorizer as! OAuth2Authorizer safariVC = try authorizer.authorizeSafariEmbedded(from: self,at: url!) } /*catch error*/ } } func callBackOAuth(authParameters:OAuth2JSON!, error: OAuth2Error!){ let appDelegate = UIApplication.shared.delegate as! AppDelegate if (error == nil && appDelegate.oauth2!.accessToken != nil){//OAuth授权成功 self.keychain!.set(appDelegate.oauth2!.accessToken!,forKey:"Token") appDelegate.reloadView() }else {/*错误处理逻辑*/} }
2. AppDelegate的URL回调处理
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { let components = URLComponents(url: url, resolvingAgainstBaseURL: false) let site=components?.host if site == "oauth"{//OAuth流程结束 if components?.path == "/callback" { let viewController = self.window?.rootViewController as! ViewController self.oauth2!.handleRedirectURL(url) viewController.hideSafariView() } } return true }
3. ViewController的viewDidAppear方法
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let appDelegate = UIApplication.shared.delegate as! AppDelegate //初始化OAuth2配置参数 appDelegate.oauth2 = OAuth2CodeGrant(settings: OAuthParams ) appDelegate.oauth2!.authConfig.authorizeContext = KronosWebsite?.window appDelegate.oauth2!.useKeychain = false appDelegate.oauth2!.authConfig.authorizeEmbeddedAutoDismiss = true appDelegate.oauth2!.logger = OAuth2DebugLogger(.debug) appDelegate.oauth2!.afterAuthorizeOrFail = self.callBackOAuth let token=self.keychain!.get("Token") if(token == nil){//未找到Token,启动OAuth流程 runOauth() } }
4. 尝试过的DispatchGroup方案(未成功)
// runOauth中添加 groupOauth.enter() // callBackOAuth中添加 groupOauth.leave() // AppDelegate的application方法中修改为 viewController.groupOauth.notify(queue: DispatchQueue.main) { self.oauth2!.handleRedirectURL(url) viewController.hideSafariView() }
解决方案
方案1:调整Safari关闭与Token存储的执行顺序
问题核心是AppDelegate中先执行了hideSafariView和页面刷新,而callBackOAuth(Token存储逻辑)是handleRedirectURL内部异步触发的,导致时序颠倒。
修改步骤:
- 移除AppDelegate中的Safari关闭调用,仅保留URL处理:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { let components = URLComponents(url: url, resolvingAgainstBaseURL: false) let site=components?.host if site == "oauth"{ if components?.path == "/callback" { self.oauth2!.handleRedirectURL(url) // 移除 viewController.hideSafariView() } } return true }
- 在回调方法中统一处理Token存储、Safari关闭和页面刷新:
func callBackOAuth(authParameters:OAuth2JSON!, error: OAuth2Error!){ let appDelegate = UIApplication.shared.delegate as! AppDelegate if (error == nil && appDelegate.oauth2!.accessToken != nil){ // 1. 存储Token self.keychain!.set(appDelegate.oauth2!.accessToken!,forKey:"Token") // 2. 关闭Safari视图 self.hideSafariView() // 3. 触发页面刷新 appDelegate.reloadView() }else { // 授权失败时也关闭Safari self.hideSafariView() /*错误处理逻辑*/ } }
方案2:避免重复初始化OAuth实例
当前viewDidAppear每次都会重新创建oauth2实例,可能导致回调绑定混乱。将实例初始化移到AppDelegate启动方法中,仅初始化一次:
// AppDelegate.swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 初始化OAuth2实例 oauth2 = OAuth2CodeGrant(settings: OAuthParams) oauth2!.authConfig.authorizeContext = KronosWebsite?.window oauth2!.useKeychain = false oauth2!.authConfig.authorizeEmbeddedAutoDismiss = true oauth2!.logger = OAuth2DebugLogger(.debug) return true }
修改viewDidAppear,仅绑定回调和检查Token:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let appDelegate = UIApplication.shared.delegate as! AppDelegate // 仅绑定回调,不再重复初始化实例 appDelegate.oauth2!.afterAuthorizeOrFail = self.callBackOAuth let token=self.keychain!.get("Token") if(token == nil){ runOauth() } }
内容的提问来源于stack exchange,提问作者Camille Gallet
相关产品推荐
相关产品推荐

