前言
Swift 语言,怎么说呢,有一种先接受后排斥,又欢迎的感觉,纵观国外大牛开源框架或项目演示,Swift几乎占据了多半,而国内虽然出现很多相关技术介绍和教程,但是在真正项目开发中使用的占据很少部分,原因一是目前熟练它的开发者并不多,二是版本不太稳定,还需要更成熟可靠的版本支持,但总之未来还是很有前景的,深有体会,不管是代码量还是编译效率,以及语言特性,现代性都优于Object-C,估计后续会被苹果作为官方开发语言,值得期待。
走起
鉴于此,笔者将之前用Object-C写的SSO授权登录:微信,QQ和微博,重新用Swift语言写一遍,以便需要的朋友参考,算是SSO授权登录的姊妹篇;
一、总体架构
1、引入第三方库
除了必须引入对应的登录SDK外,额外引入了SDWebImage,SVProgressHUD,看名字大家都明白吧,引入登录SDK请各自看官方的开发文档,需要加入什么系统库文件,需要配置Other Linker Flags 等,请参考各自官方文档即可;
2、配置连接桥文件
因为创建的工程是基于Swift语言,目前官方SDK和其它三方库都是用OC写的,所以为了在swift中调用oc代码,需要配置连接桥文件Bridging-Header.h,搜索objective-C bridging Header健,然后在值里面输入XXXLogin/Bridging-Header.h
,注意是绝对路径,里面可以输入需要调用的头文件,如
#import "WXApi.h"
#import "SVProgressHUD.h"
#import "UIImageView+WebCache.h"
3、配置工程
因为是SSO跳转方式,需要配置URL Schemes,以便程序返回识别宿主程序,配置方法很简单,参考各自文档即可,在info里面可以可视化添加,各自的key值采用官方demo所提供;
二、微信
1、注册
func application(application: UIApplication,didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
//向微信注册
WXApi.registerApp(kWXAPP_ID)
return true
}
2、授权登录
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self,selector:"onRecviceWX_CODE_Notification:",name: "WX_CODE",object: nil)
let sendBtn:UIButton = UIButton()
sendBtn.frame = CGRectMake(30,100,kIPHONE_WIDTH-60,40)
sendBtn.backgroundColor = UIColor.redColor()
sendBtn.setTitleColor(UIColor.whiteColor(),forState: UIControlState.Normal)
sendBtn.setTitle("Swift版本之微信授权登录",forState: UIControlState.Normal)
sendBtn.addTarget(self,action: "sendBtnClick:",forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(sendBtn)
headerImg = UIImageView(frame: CGRectMake(30,160,120,120))
headerImg.backgroundColor = UIColor.yellowColor()
self.view.addSubview(headerImg)
nicknameLbl.frame = CGRectMake(170,kIPHONE_WIDTH-60-140,40)
nicknameLbl.backgroundColor = UIColor.lightGrayColor()
nicknameLbl.textColor = UIColor.purpleColor()
nicknameLbl.textAlignment = NSTextAlignment.Center
self.view.addSubview(nicknameLbl)
}
func sendBtnClick(sneder:UIButton){
sendWXAuthRequest()
}
//微信登录 第一步
func sendWXAuthRequest(){
let req : SendAuthReq = SendAuthReq()
req.scope = "snsapi_userinfo,snsapi_base"
WXApi .sendReq(req)
}
3、回调
func onResp(resp: BaseResp!) {
/* ErrCode ERR_OK = 0(用户同意) ERR_AUTH_DENIED = -4(用户拒绝授权) ERR_USER_CANCEL = -2(用户取消) code 用户换取access_token的code,仅在ErrCode为0时有效 state 第三方程序发送时用来标识其请求的唯一性的标志,由第三方程序调用sendReq时传入,由微信终端回传,state字符串长度不能超过1K lang 微信客户端当前语言 country 微信用户当前国家信息 */
// var aresp resp :SendAuthResp!
var aresp = resp as! SendAuthResp
// var aresp1 = resp as? SendAuthResp
if (aresp.errCode == 0)
{
println(aresp.code)
//031076fd11ebfa5d32adf46b37c75aax
var dic:Dictionary<String,String>=["code":aresp.code];
let value = dic["code"]
println("code:\(value)")
NSNotificationCenter.defaultCenter().postNotificationName("WX_CODE",object: nil,userInfo: dic)
}
}
4、获取用户信息
//微信回调通知,获取code 第二步
func onRecviceWX_CODE_Notification(notification:NSNotification){
SVProgressHUD.showSuccessWithStatus("获取到code",duration: 1)
var userinfoDic : Dictionary = notification.userInfo!
let code: String = userinfoDic["code"] as! String
print("Recevice Code: \(code)")
self.getAccess_token(code)
}
//获取token 第三步
func getAccess_token(code :String){
//https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
var requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=\(kWXAPP_ID)&secret=\(kWXAPP_SECRET)&code=\(code)&grant_type=authorization_code"
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),{
var requestURL: NSURL = NSURL(string: requestUrl)!
var data = NSData(contentsOfURL: requestURL,options: NSDataReadingOptions(),error: nil)
dispatch_async(dispatch_get_main_queue(),{
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data!,options: NSJSONReadingOptions.MutableContainers,error: nil) as! NSDictionary
println("Recevice Token: \(jsonResult)")
SVProgressHUD.showSuccessWithStatus("获取到Token和openid",duration: 1)
let token: String = jsonResult["access_token"] as! String
let openid: String = jsonResult["openid"] as! String
self.getUserInfo(token,openid: openid)
})
})
}
//获取用户信息 第四步
func getUserInfo(token :String,openid:String){
// https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
var requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=\(token)&openid=\(openid)"
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,error: nil) as! NSDictionary
print("Recevice UserInfo: \(jsonResult)")
/* Recevice UserInfo: { city = Chaoyang; country = CN; headimgurl = "http://wx.qlogo.cn/mmopen/FrdAUicrPIibcpGzxuD0kjfssQogj3icL8QTJQYUCLpgzSnvY6rJFGORreicpuiaPCzojwNlsXq4ibbc8e3gGFricWqJU5ia7ibicLVhfT/0"; language = "zh_CN"; nickname = "\U706b\U9505\U6599"; openid = "oyAaTjkR8T6kcKWyA4VPYDa_Wy_w"; privilege = ( ); province = Beijing; sex = 1; unionid = "o1A_Bjg52MglJiEjhLmB8SyYfZIY"; } */
SVProgressHUD.showSuccessWithStatus("获取到用户信息",duration: 1)
let headimgurl: String = jsonResult["headimgurl"] as! String
let nickname: String = jsonResult["nickname"] as! String
self.headerImg.sd_setImageWithURL(NSURL(string: headimgurl))
self.nicknameLbl.text = nickname
})
})
}
5、跳转
//微信的跳转回调
func application(application: UIApplication,openURL url: NSURL,sourceApplication: String?,annotation: AnyObject?) -> Bool {
return WXApi.handleOpenURL(url,delegate: self)
}
func application(application: UIApplication,handleOpenURL url: NSURL) -> Bool {
return WXApi.handleOpenURL(url,delegate: self)
}
三、QQ
1、注册
func sendBtnClick(sneder:UIButton){
sendQQAuthRequest()
}
//第一步 QQ登录
func sendQQAuthRequest(){
tencentOAuth = TencentOAuth(appId: kQQAPP_ID,andDelegate: self)
var permissions = [kOPEN_PERMISSION_GET_INFO,kOPEN_PERMISSION_GET_USER_INFO,kOPEN_PERMISSION_GET_SIMPLE_USER_INFO]
tencentOAuth.authorize(permissions,inSafari: false)
}
2、授权登录
如上
3、回调
//第二步 登录成功回调
func tencentDidLogin() {
let accessToken = tencentOAuth.accessToken
println("accessToken:\(accessToken)") //641B23508B62392C52D6DFADF67FAA9C
getUserInfo()
}
//失败
func tencentDidNotLogin(cancelled: Bool) {
println("登录失败了")
}
//无网络
func tencentDidNotNetWork() {
println("没有网络")
}
4、获取用户信息
//第三步 获取用户信息
func getUserInfo(){
SVProgressHUD.showWithStatus("正在获取用户信息...")
tencentOAuth.getUserInfo()
}
//第四步 在获取用户回调中获取用户信息
func getUserInfoResponse(response: APIResponse!) {
SVProgressHUD.dismissWithSuccess("获取用户信息成功",afterDelay: 1)
var dic:Dictionary = response.jsonResponse
println("dic:\(dic)")
// [is_lost: 0,figureurl: http://qzapp.qlogo.cn/qzapp/222222/C5527A2F775D9EA7C20317128FAC202B/30,vip: 0,is_yellow_year_vip: 0,province: 北京,ret: 0,is_yellow_vip: 0,figureurl_qq_1: http://q.qlogo.cn/qqapp/222222/C5527A2F775D9EA7C20317128FAC202B/40,yellow_vip_level: 0,level: 0,figureurl_1: http://qzapp.qlogo.cn/qzapp/222222/C5527A2F775D9EA7C20317128FAC202B/50,city: 海淀,figureurl_2: http://qzapp.qlogo.cn/qzapp/222222/C5527A2F775D9EA7C20317128FAC202B/100,nickname: 竹中雨滴,msg:,gender: 男,figureurl_qq_2: http://q.qlogo.cn/qqapp/222222/C5527A2F775D9EA7C20317128FAC202B/100]
refeshUserInfo(dic)
}
//第五步 刷新用户界面
func refeshUserInfo(dic : NSDictionary){
let headimgurl: String = dic["figureurl_qq_2"] as! String
let nickname: String = dic["nickname"] as! String
self.headerImg.sd_setImageWithURL(NSURL(string: headimgurl))
self.nicknameLbl.text = nickname
}
5、跳转
func application(application: UIApplication,openURL url: NSURL,annotation: AnyObject?) -> Bool {
return TencentOAuth.HandleOpenURL(url)
}
func application(application: UIApplication,handleOpenURL url: NSURL) -> Bool {
return TencentOAuth.HandleOpenURL(url)
}
四、微博
1、注册
func application(application: UIApplication,didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
WeiboSDK.registerApp(kAppKey)
return true
}
2、授权登录
func sendBtnClick(sneder:UIButton){
sendSinaAuthRequest()
}
//第一步 微博登录
func sendSinaAuthRequest(){
var request : WBAuthorizeRequest = WBAuthorizeRequest.request() as! WBAuthorizeRequest
request.redirectURI = kRedirectURI
request.scope = "all"
request.userInfo = ["SSO_Key":"SSO_Value"]
WeiboSDK.sendRequest(request)
}
3、回调
func didReceiveWeiboRequest(request: WBBaseRequest!) {
}
func didReceiveWeiboResponse(response: WBBaseResponse!) {
if response.isKindOfClass(WBAuthorizeResponse){
if (response.statusCode == WeiboSDKResponseStatusCode.Success) {
var authorizeResponse : WBAuthorizeResponse = response as! WBAuthorizeResponse
var userID = authorizeResponse.userID
var accessToken = authorizeResponse.accessToken
println("userID:\(userID)\naccessToken:\(accessToken)")
var userInfo = response.userInfo as Dictionary
NSNotificationCenter.defaultCenter().postNotificationName("SINA_CODE",object: nil,userInfo: userInfo)
}
}
}
4、获取用户信息
//第二步 通过通知得到登录后获取的用户信息
func onRecviceSINA_CODE_Notification(notification:NSNotification)
{
SVProgressHUD.showSuccessWithStatus("获取到用户信息",duration: 1)
var userinfoDic : Dictionary = notification.userInfo!
println("userInfo:\(userinfoDic)")
/* userID:2627289515 accessToken:2.002BqnrCyY87OC80500cab28Ofqd3B userInfo: [uid: 2627289515,remind_in: 647057,scope: invitation_write,refresh_token: 2.002BqnrCyY87OC10f7877765yPietB,app: { logo = "http://ww1.sinaimg.cn/square/65745bf7jw1ea399us692j2028028glf.jpg"; name = "SDK\U5fae\U535a\U5e94\U7528demo"; },access_token: 2.002BqnrCyY87OC80500cab28Ofqd3B,expires_in: 647057 ] */
var userAppInfo: Dictionary<String,String> = userinfoDic["app"] as! Dictionary
refeshUserInfo(userAppInfo)
}
//第三步 刷新用户界面
func refeshUserInfo(dic : NSDictionary){
let headimgurl: String = dic["logo"] as! String
let nickname: String = dic["name"] as! String
self.headerImg.sd_setImageWithURL(NSURL(string: headimgurl))
self.nicknameLbl.text = nickname
}
5、跳转
func application(application: UIApplication,annotation: AnyObject?) -> Bool {
return WeiboSDK.handleOpenURL(url,delegate: self)
}
func application(application: UIApplication,handleOpenURL url: NSURL) -> Bool {
return WeiboSDK.handleOpenURL(url,delegate: self)
}
五、对比分析
1、demo情况
微博demo代码工整度完爆微信,QQ,看着很舒服,心情也不错;另外微博放在了github上面,适合pod管理,注释也极好,微信文档写的挺不错,QQ写的简直丧心病狂,需要极度耐心才能看明白,表示很无语,另外本三种方式授权登录的源代码有偿提供,如需可以邮件mmw05@163.com即可;
2、嵌入时间
微信算是很容易嵌入SDK,QQ也还可以,微博需要注意boundID有限制,微信的逻辑算是比较冗余繁琐,从授权到获取到用户信息需要很多接口,而QQ和微博可以直接从授权登陆回调中获取到,是比较便捷的,从上面代码可以看出来;
后记
从objective-C到Swift,苹果力求简约,但又不简单,现代化的语言,必然在性能各方面优于传统,只是需要时间和更多的考验,作为开发者,多一个选择,岂不更好!
附图: