iOS Swift开发:通过deeplink唤起App如何获取返回登录结果
跨App Deeplink登录结果回传实现方案
核心逻辑是通过双向自定义URL Scheme完成跳转和参数传递,不需要调用私有API,符合App Store审核规则。
前置配置
- 给第一款App新增专属回调URL Scheme:打开工程配置的「Info - URL Types」项,新增标识(例如
firstapp),确保和其他App Scheme不重名,配置完成后第一款App即可被firstapp://开头的链接唤起。 - 两个App都在
Info.plist中添加跳转白名单LSApplicationQueriesSchemes,把对方的Scheme加入数组,否则iOS 9+系统会拦截跳转请求:
<key>LSApplicationQueriesSchemes</key> <array> <!-- 第一款App中加入第二款App的Scheme --> <string>lg</string> <!-- 第二款App中加入第一款App的回调Scheme --> <string>firstapp</string> </array>
第一款App修改点
1. 调整唤起第二款App的逻辑
原有跳转逻辑没有携带回调标识,需要把自身的回调Scheme作为参数拼到跳转链接里,告诉第二款App登录完成后的跳转目标:
// FIRST APP 登录按钮点击逻辑 let callbackScheme = "firstapp" guard let url = URL(string: "lg://loginapp.com/login?callback=\(callbackScheme)") else { return } if #available(iOS 10.0, *) { UIApplication.shared.open(url, options: [:]) } else { UIApplication.shared.openURL(url) }
2. 实现回调参数接收逻辑
根据工程是否使用Scene,在对应生命周期方法中解析第二款App回传的参数:
// 未使用Scene的旧版本工程,在AppDelegate中实现 func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { // 仅处理自身回调Scheme触发的唤起 guard url.scheme == "firstapp" else { return false } // 解析URL参数 let components = URLComponents(url: url, resolvingAgainstBaseURL: false) let queryItems = components?.queryItems ?? [] let params = Dictionary(uniqueKeysWithValues: queryItems.map { ($0.name, $0.value ?? "") }) // 处理登录结果 if params["status"] == "success", let token = params["token"] { // 在此写入登录成功逻辑:存储token、刷新UI等 print("收到回传登录凭证:\(token)") } else { // 处理登录失败、用户取消场景 let errorMsg = params["error"] ?? "登录失败" print(errorMsg) } return true } // iOS13+使用Scene的工程,需要在SceneDelegate中补充实现 @available(iOS 13.0, *) func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { guard let url = URLContexts.first?.url else { return } guard url.scheme == "firstapp" else { return } // 参数解析逻辑和上述AppDelegate中完全一致 let components = URLComponents(url: url, resolvingAgainstBaseURL: false) let queryItems = components?.queryItems ?? [] let params = Dictionary(uniqueKeysWithValues: queryItems.map { ($0.name, $0.value ?? "") }) if params["status"] == "success", let token = params["token"] { print("收到回传登录凭证:\(token)") } }
第二款App修改点
1. 删除原有私有API调用
注意:原有代码中调用的NSXPCConnection.suspend属于未公开私有API,上架审核有极大概率被拒,必须替换为官方允许的openURL跳转方案,系统会自动将当前App退至后台,切回目标App。
2. 解析入参保存回调地址
在被第一款App唤起时,从链接参数中取出回调Scheme暂存:
// SECOND APP 定义全局变量存储回调地址 var callbackScheme: String? // 在openURL代理方法中解析入参 func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { if url.scheme == "lg" { let components = URLComponents(url: url, resolvingAgainstBaseURL: false) callbackScheme = components?.queryItems?.first(where: { $0.name == "callback" })?.value // 在此写入跳转自身登录页的逻辑 return true } return false }
3. 登录完成后回传结果并跳回
登录流程结束后,把token、登录状态等参数拼到回调链接中,直接唤起第一款App完成回传:
// SECOND APP 登录完成回调方法,替换原有suspend调用 func loginDidFinish(token: String?, error: Error?) { guard let callbackScheme = callbackScheme else { return } var callbackUrlStr = "\(callbackScheme)://logincallback?" // 拼接结果参数 if let token = token { callbackUrlStr += "status=success&token=\(token)" } else { callbackUrlStr += "status=fail&error=\(error?.localizedDescription ?? "未知错误")" } // 对参数做URL编码,避免特殊字符导致链接失效 guard let encodedUrlStr = callbackUrlStr.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let callbackUrl = URL(string: encodedUrlStr) else { return } // 跳回第一款App if #available(iOS 10.0, *) { UIApplication.shared.open(callbackUrl, options: [:]) } else { UIApplication.shared.openURL(callbackUrl) } }
优化建议
- 如果对登录安全性要求较高,不要直接把正式token明文拼在URL中,可以先回传一次性临时授权码,第一款App收到授权码后通过服务端接口换取正式token,降低凭证被劫持的风险。
- 可以给回调链接增加来源校验,避免其他恶意App伪造回调链接窃取登录状态。
内容的提问来源于stack exchange,提问作者Urma
相关产品推荐
相关产品推荐

