背景
在现代移动应用中,集成第三方登录已经成为一种常见的需求,它不仅能提高用户体验,还能减少用户注册的阻力。我们选择了 Twitter 作为示例,但类似的步骤和逻辑也适用于其他第三方登录服务。希望这篇博客能为你提供清晰的指导,并帮助你在应用中成功实现第三方登录。
Twitter登录与其它三方登录不同,比如Facebook官方提供了完整的SDK可以直接使用,业务场景中有遇到的话难免会有点不知所措,下面就介绍一下Twitter登录的实现方案。
实现方案一
虽然Twitter没有提供官方的SDK但是也在开发者网站上也推荐了两个github的库。
https://developer.x.com/en/docs/twitter-api/tools-and-libraries/v2
我们以TwitterAPIKit为例:
1.点击连接下载TwitterAPIKit到本地。
2.打开自己的项目点击file->Add Package Dependenceis将刚刚下载的Package添加到项目。
添加后项目结构如下:
3.Info.plist文件增加scheme
允许回调到你的APP
允许打开其它APP
4.代码实现
声明一些必要属性
class ViewController: UIViewController, ASWebAuthenticationPresentationContextProviding {
/// 替换成你自己的consumerKey
let consumerKey = "consumerKey"
/// 替换成你自己的consumerSecret
let consumerSecret = "consumerSecret"
/// callbackURL
var callbackURL:String!
/// scheme
var scheme:String!
var client:TwitterAPIClient!
override func viewDidLoad() {
.....
}
}
consumerKey和consumerSecret是在开发平台创建自己应用后可以获取到的两个数据。
callbackURL和scheme需要自己进行拼接
callbackURL = "twitterkit-\(consumerKey)://"
scheme = "twitterkit-\(consumerKey)"
点击登录按钮后创建TwitterAPIClient的实例client,并发起post请求获取用户token和secret
@objc func loginAction() {
callbackURL = "twitterkit-\(consumerKey)://"
scheme = "twitterkit-\(consumerKey)"
client = TwitterAPIClient(.oauth10a(.init(consumerKey: consumerKey, consumerSecret: consumerSecret, oauthToken: nil, oauthTokenSecret: nil)))
client.auth.oauth10a.postOAuthRequestToken(.init(oauthCallback: callbackURL)).responseObject {[weak self] response in
guard let self = self else { return }
do {
let success = try response.result.get()
let url = self.client.auth.oauth10a.makeOAuthAuthorizeURL(.init(oauthToken: success.oauthToken))!
let session = ASWebAuthenticationSession(url: url, callbackURLScheme: scheme) { url, error in
// url is "twitter-api-kit-ios-sample://?oauth_token=<string>&oauth_verifier=<string>"
guard let url = url else {
if let error = error {
print(error)
}
return
}
print("URL:", url)
let component = URLComponents(url: url, resolvingAgainstBaseURL: false)
guard let oauthToken = component?.queryItems?.first(where: { $0.name == "oauth_token"} )?.value,
let oauthVerifier = component?.queryItems?.first(where: { $0.name == "oauth_verifier"})?.value else {
print("Invalid URL")
return
}
self.client.auth.oauth10a.postOAuthAccessToken(.init(oauthToken: oauthToken, oauthVerifier: oauthVerifier))
.responseObject { response in
do {
let token = try response.result.get()
let oauthToken = token.oauthToken
let oauthTokenSecret = token.oauthTokenSecret
print("result : \(oauthToken), \(oauthTokenSecret)")
} catch {
print(error)
}
}
}
session.presentationContextProvider = self
session.prefersEphemeralWebBrowserSession = true
session.start()
} catch {
print(error)
}
}
}
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
return view.window!
}
将获取到的token和screte传递给服务端,完成Twitter登录。
但是这里面有个问题,由于我们使用ASWebAuthenticationSession,它主要用于处理 OAuth 和 OpenID Connect 等 web 基于身份验证的流程。ASWebAuthenticationSession 让应用程序可以安全地打开一个浏览器界面(使用 Safari 浏览器)来完成认证过程,并在认证完成后将结果返回给应用程序。
所以即使安装了TwitterAPP也不能调起TwitterAPP,整个认证过程都是在自己的APP内进行。
实现方案二
再介绍一种更简单的实现方式,不需要借助任何三方代码,直接使用ASWebAuthenticationSession或者UIApplication.shared.open的方式打开认证页面。
为了可以调起Twitter我们这里使用UIApplication.shared.open的方式。
info.plist文件的设置与上面相同,这里就不过多介绍了。
代码实现
我们为它单独创建了一个单利
class CSTwitterHelper: NSObject {
static let shared = CSTwitterHelper()
....
}
同样需要使用Consumer_Key和Consumer_Secret。
let Twitter_Consumer_Key = "pxvBpjJMk4OwxA1rB7R3OnSEN"
let Twitter_Consumer_Secret = "EQzLWzeIBqZzOr1WN6g4V6aH0WUNiDwFx6aQkGtG20E2xLwCaD"
var successCompletion: ((_ accessToken:String,_ accessTokenSecret:String) -> Void)?
var failure: ((_ error:Error) -> Void)?
点击登录时,直接使用UIApplication.shared.open方式打开拼接的url
/// twitter登录
/// - Parameters:
/// - successCompletion: 成功回调
/// - fairlure: 失败回调
func twitterLogin(successCompletion: @escaping (_ accessToken:String,_ accessTokenSecret:String) -> Void,fairlure: @escaping (_ error:Error) -> Void) {
let url = "twitterauth://authorize?consumer_key=\(Twitter_Consumer_Key)&consumer_secret=\(Twitter_Consumer_Secret)&oauth_callback=twitterkit-\(Twitter_Consumer_Key)"
UIApplication.shared.open(URL(string: url)!, options: [:]) { success in
if !success {
let error = NSError(domain: "twitter", code: 101, userInfo: [NSLocalizedDescriptionKey:"Cannot open twitter app"])
fairlure(error)
}
}
self.successCompletion = successCompletion
self.failure = fairlure
}
在AppDelegate中设置回调,如果你保留了SceneDelegate也需要设置回调。
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
// twitter
if url.scheme == "twitterkit-\(CSTwitterHelper.shared.Twitter_Consumer_Key)" {
return CSTwitterHelper.shared.twitterApplication(app, open: url, options: options)
}
}
在回调中获取我们需要的token和secret传递给服务端。
/// twitter登录回调
/// - Parameters:
/// - app: UIApplication
/// - url: URL
/// - options: [UIApplication.OpenURLOptionsKey : Any]
/// - Returns: Bool
func twitterApplication(_ app: UIApplication,open url: URL,options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let isCanceled = (url.host == nil)
if isCanceled {
// 用户取消
let error = NSError(domain: "twitter", code: 100, userInfo: [NSLocalizedDescriptionKey:"User canceled"])
self.failure?(error)
return false
}
let params = url.queryParams
let secret = params["secret"]
let token = params["token"]
let userName = params["username"]
if let token = token,let secret = secret {
self.successCompletion?(token,secret)
} else {
let error = NSError(domain: "twitter", code: 102, userInfo: [NSLocalizedDescriptionKey:"params error"])
self.failure?(error)
}
return true
}
其中url.queryParams是为URL添加了一个扩展方法,用来获取参数。
extension URL {
/// 获取参数
var queryParams: [String : String] {
guard let host = self.host else { return [:] }
return host.split(separator: "&").reduce(into: [:]) { (result, parameter) in
let keyValue = parameter.split(separator: "=")
result[String(keyValue[0])] = String(keyValue[1])
}
}
}
结语
对客户端来说Twitter登录,只需要获取到token和secret就算完成了,任何一种方法只要能从Twitter获取到这两个数据就算登录成功了。
感谢你的阅读,如果你有任何问题或需要进一步的帮助,请随时留言交流。祝你开发顺利,项目成功!