三方平台SDK初始化
首先导入头文件
//腾讯SDK头文件
#import <TencentOpenAPI/TencentOAuth.h>
#import <TencentOpenAPI/QQApiInterface.h>
//微信SDK头文件
#import "WXApi.h"
//新浪微博SDK头文件
#import "WeiboSDK.h"
各个平台的APPID,提前在开放平台申请好,这里直接使用。iOS平台QQ和微博支持网页可SSO授权,微信仅支持SSO授权。这里出现的类似SINAWEIBO_APPID
的一些宏,新建了一个GMThirdPartyDefine.h,将这些宏、以及枚举集中放到这个文件里。
枚举文件具体内容如下:
#ifndef GMThirdPartyDefine_h
#define GMThirdPartyDefine_h
typedef NS_ENUM(NSInteger, GMShareResponseState) {
GMShareResponseStateBegin = -1,
GMShareResponseStateSuccess = 0,
GMShareResponseStateFail = 1,
GMShareResponseStateCancel = 2,
GMShareResponseStateBack = 3,
};
typedef NS_ENUM(NSInteger, GMSharePlatform) {
GMSharePlatformUnknown = -1,
GMSharePlatformSinaWeibo = 0, /**< 新浪微博 */
GMSharePlatformQQSpace = 1, /**< QQ空间 */
GMSharePlatformQQFriend = 2, /**< QQ好友 */
GMSharePlatformWechatSession = 3, /**< 微信好友 */
GMSharePlatformWechatTimeline = 4, /**< 微信朋友圈 */
GMSharePlatformFav = 5, /**< 收藏 */
GMSharePlatformDelete = 6, /**< 删除 */
GMSharePlatformCopyLink = 7, /**< 复制链接 */
GMSharePlatformRefresh = 7, /**< 刷新 */
GMSharePlatformReport = 8, /**< 举报 */
GMSharePlatformCancelFollow = 9, /**< 圈子取消关注 */
GMSharePlatformWechatSessionSnapShot = 10, /**< 微信好友快照 */
GMSharePlatformWechatTimelineSnapShot = 11, /**< 微信朋友圈快照 */
GMSharePlatformWechat = 100, /**< 微信平台*/
GMSharePlatformQQ = 101, /**< QQ平台*/
};
/**
* 内容类型
*/
typedef NS_ENUM(NSUInteger, GMShareContentType){
/**
* 自动适配类型,视传入的参数来决定
*/
GMShareContentTypeAuto = 0,
/**
* 文本
*/
GMShareContentTypeText = 1,
/**
* 图片
*/
GMShareContentTypeImage = 2,
/**
* 网页
*/
GMShareContentTypeWebPage = 3,
/**
* 应用
*/
GMShareContentTypeApp = 4,
/**
* 音频
*/
GMShareContentTypeAudio = 5,
/**
* 视频
*/
GMShareContentTypeVideo = 6,
/**
* 文件类型(暂时仅微信可用)
*/
GMShareContentTypeFile = 7,
//图片类型 仅FacebookMessage 分享图片并需要明确结果时 注此类型分享后不会显示应用名称与icon
//v3.6.2 增加
GMShareContentTypeFBMessageImages = 8,
//图片类型 仅FacebookMessage 分享视频并需要明确结果时 注此类型分享后不会显示应用名称与icon
//所分享的视频地址必须为相册地址
//v3.6.2 增加
GMShareContentTypeFBMessageVideo = 9,
//3.6.3 小程序分享(暂时仅微信可用)
GMShareContentTypeMiniProgram = 10
};
#define kQQLogin @"kQQLogin"
#define kSinaLogin @"kSinaLogin"
#define kWechatLogin @"kWechatLogin"
//QQ好友和朋友圈会掉的url完全一样,所以没必要区分 QQ41A1DB6D://response_from_qq?source=qq&source_scheme=mqqapi&error=0&version=1&sdkv=3.1
#define kQQShare @"kQQShare"
#define kSinaShare @"kSinaShare"
#define kWechatShare @"kWechatShare"
#define kThirdPartyLoginBack @"kThirdPartyLoginBack"
#define kThirdPartyShareBack @"kThirdPartyShareBack"
#define WEICHAT_BASE_URL @"https://api.weixin.qq.com/sns"
//有关登陆授权的一些key
#ifdef APPSTORE
#define WEIXIN_APPID @"wx4326da3ad2429149" //WeiXin appId
#define WEIXIN_SECRET @"971147c78f55c3a657394a7a31f4266a" //WeiXin secret
#else
#define WEIXIN_APPID @"wx403cd28c142c7d87" //WeiXin appId
#define WEIXIN_SECRET @"035af39ad74bbc01b7517a291c836827" //WeiXin secret
#endif
#define QQ_APPID @"1101126509" //QQ APPID
#define QQ_HEX_APPID @"41A1DB6D" //16进制的QQ appid
#define QQ_APPKEY @"yD4Eu3WZX6egmrMU" //QQ APPKEY
#define SINAWEIBO_APPID @"2387882244" //Sina Weibo APPKEY
#define SINAWEIBO_APPKEY @"8480ee24118f0dec4f71e0ca1ba8638e" //Sina Weibo secret
#define SINAWEIBO_REDIRECTURI @"http://www.wanmeizhensuo.com/api/user/weibo/auth/"
#endif /* GMThirdPartyDefine_h */
我们可以在application: willFinishLaunchingWithOptions:中做SDK的初始化工作,这里耗时很短,放在这里是可以的。对启动时间影响可以忽略不计。
- (void)setupThirdPartySDK{
//微博SDK初始化
[WeiboSDK enableDebugMode:YES];
[WeiboSDK registerApp:SINAWEIBO_APPID];
//腾讯SDK初始化,这里delegate是 [GMThirdPartyOauthManager shareInstance]
[GMThirdPartyOauthManager shareInstance]
[GMThirdPartyOauthManager shareInstance].tencentOAuth = [[TencentOAuth alloc] initWithAppId:QQ_APPID andDelegate:[GMThirdPartyOauthManager shareInstance]];
//微信SDK初始化
[WXApi registerApp:WEIXIN_APPID enableMTA:YES];
//向微信注册支持的文件类型
UInt64 typeFlag = MMAPP_SUPPORT_TEXT | MMAPP_SUPPORT_PICTURE | MMAPP_SUPPORT_LOCATION | MMAPP_SUPPORT_VIDEO |MMAPP_SUPPORT_AUDIO | MMAPP_SUPPORT_WEBPAGE | MMAPP_SUPPORT_DOC | MMAPP_SUPPORT_DOCX | MMAPP_SUPPORT_PPT | MMAPP_SUPPORT_PPTX | MMAPP_SUPPORT_XLS | MMAPP_SUPPORT_XLSX | MMAPP_SUPPORT_PDF;
[WXApi registerAppSupportContentFlag:typeFlag];
}
GMThirdPartyOauthManager这个是用来处理授权的一个单例对象。在SSO授权的时候会调到对应的第三方APP,授权成功/失败,返回应用时候会执行下面的方法,iOS8和iOS8以上版本略有区别。在AppDelegate中需要实现以下方法。
// iOS 8 会走这个
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{
return [self commmonHandleOpenURL:url sourceApplication:sourceApplication];
}
// iOS 9、10、11 会走这个
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options{
return [self commmonHandleOpenURL:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]];
}
- (BOOL)commmonHandleOpenURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication {
if ([string hasPrefix:[NSString stringWithFormat:@"wb%@",SINAWEIBO_APPID]]) {
//微博delegate同样也是[GMThirdPartyOauthManager shareInstance]
return [WeiboSDK handleOpenURL:url delegate:[GMThirdPartyOauthManager shareInstance]];
} else if([string hasPrefix:[NSString stringWithFormat:@"tencent%@",QQ_APPID]]) {
return [TencentOAuth HandleOpenURL:url];
} else if([string hasPrefix:[NSString stringWithFormat:@"QQ%@",QQ_HEX_APPID]]){
//QQ_HEX_APPID是16进制的QQ appid
NSDictionary* params = [string urlQueryToDictionary];
NSInteger error = [params[@"error"] integerValue];
if (error == 0) {
[[NSNotificationCenter defaultCenter] postNotificationName:kQQShare object:nil userInfo:@{@"state": @(GMShareResponseStateSuccess)}];
} else if (error == -4){
[[NSNotificationCenter defaultCenter] postNotificationName:kQQShare object:nil userInfo:@{@"state": @(GMShareResponseStateCancel)}];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:kQQShare object:nil userInfo:@{@"state": @(GMShareResponseStateFail)}];
}
return [TencentOAuth HandleOpenURL:url];
}else{
//微新的delegate同样也是[GMThirdPartyOauthManager shareInstance]
return [WXApi handleOpenURL:url delegate:[GMThirdPartyOauthManager shareInstance]];
}
return YES;
}
这里腾讯写了两个分支是为了兼容旧版本的手机QQ,需要增加 URL Scheme,QQ + 十六进制新AppId,不足八位在首部补0。(如 appid=222222 则 scheme=QQ0003640E)。我们可以看到我这里将三个平台的SDK的delegate统一设置成了[GMThirdPartyOauthManager shareInstance],目的是避免代码散落各处,便于统一处理管理授权、分享的回调。
接下来就是真正的调用登陆授权
+ (void)loginByPlatform:(GMSharePlatform)platform
onUserSync:(GMThirdPartyUserHandler)userHandler{
[GMThirdPartyOauthManager shareInstance].user.platformType = platform;
if (platform == GMSharePlatformQQ) {
NSArray* permissions = [NSArray arrayWithObjects:
kOPEN_PERMISSION_GET_USER_INFO,
kOPEN_PERMISSION_GET_SIMPLE_USER_INFO,
nil];
[[GMThirdPartyOauthManager shareInstance].tencentOAuth setAuthShareType: AuthShareType_QQ];
[[GMThirdPartyOauthManager shareInstance].tencentOAuth authorize:permissions inSafari:NO];
__block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:kQQLogin object:nil queue: [NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
GMShareResponseState state = (GMShareResponseState )[note.userInfo[@"state"] integerValue];
userHandler(state, note.object);
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
} else if (platform == GMSharePlatformWechat) {
if ([WXApi isWXAppInstalled]){
[GMThirdPartyLoginHelper wechatLogin];
__block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:kWechatLogin object:nil queue: [NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
GMShareResponseState state = (GMShareResponseState )[note.userInfo[@"state"] integerValue];
userHandler(state, note.object);
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
}
} else if (platform == GMSharePlatformSinaWeibo){
WBAuthorizeRequest *request = [WBAuthorizeRequest request];
request.redirectURI = SINAWEIBO_REDIRECTURI ;
//http://open.weibo.com/wiki/Scope
request.scope = @"all";
request.userInfo = @{@"flag": @"authorize"};
[WeiboSDK sendRequest:request];
__block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:kSinaLogin object:nil queue: [NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
GMShareResponseState state = (GMShareResponseState )[note.userInfo[@"state"] integerValue];
userHandler(state, note.object);
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
}
}
+ (void)wechatLogin {
if ([WXApi isWXAppInstalled]){
//构造SendAuthReq结构体
SendAuthReq* req = [[SendAuthReq alloc ] init];
req.scope = @"snsapi_userinfo" ;
req.state = @"Gengmei" ;
//第三方向微信终端发送一个SendAuthReq消息结构
[WXApi sendReq:req];
}
}
+ (void)thirdPartyLogout {
if ([GMThirdPartyOauthManager shareInstance].user.platformType == GMSharePlatformQQ){
[GMThirdPartyOauthManager shareInstance].tencentOAuth = [[TencentOAuth alloc] initWithAppId:QQ_APPID andDelegate:[GMThirdPartyOauthManager shareInstance]];
}
}
授权操作结束后,从第三方App回到自己的App会收到一系列的回调,统一在GMThirdPartyOauthManager中处理
+ (instancetype)shareInstance
{
static dispatch_once_t onceToken;
static id instance = nil;
dispatch_once(&onceToken, ^{
instance = [[[self class] alloc] init];
});
return instance;
}
- (instancetype)init{
if (self = [super init]) {
_user = [[GMThirdPartyUser alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification object:@{@"key":@"useForOauth"}];
}
return self;
}
- (void)applicationDidBecomeActive:(NSNotification *)noti {
NSString *key = noti.object[@"key"];
if (![_user.uid isNonEmpty] && [key isEqualToString:@"useForOauth"]){
if (_user.platformType == GMSharePlatformQQ) {
[[NSNotificationCenter defaultCenter] postNotificationName:kQQLogin object:nil userInfo:@{@"state": @(GMShareResponseStateBack)}];
} else if (_user.platformType == GMSharePlatformSinaWeibo){
[[NSNotificationCenter defaultCenter] postNotificationName:kSinaLogin object:nil userInfo:@{@"state": @(GMShareResponseStateBack)}];
} else if (_user.platformType == GMSharePlatformWechat){
[[NSNotificationCenter defaultCenter] postNotificationName:kWechatLogin object:nil userInfo:@{@"state": @(GMShareResponseStateBack)}];
}
}
}
#pragma mark -- WeiboSDKDelegate
- (void)didReceiveWeiboRequest:(WBBaseRequest *)request{}
//微博登陆、分享成功后的回调
- (void)didReceiveWeiboResponse:(WBBaseResponse *)response{
NSString* accessToken = @"";
NSString* userId = @"";
if ([response isKindOfClass:WBSendMessageToWeiboResponse.class]){
WBSendMessageToWeiboResponse* sendMessageToWeiboResponse = (WBSendMessageToWeiboResponse*)response;
if ([response.userInfo[@"flag"] isEqualToString:@"authorize"]) {
//登陆授权成功
accessToken = [sendMessageToWeiboResponse.authResponse accessToken];
userId = [sendMessageToWeiboResponse.authResponse userID];
[self requstWeiBoUseInfoByAccessToken:accessToken userId:userId];
} else if ([response.userInfo[@"flag"] isEqualToString:@"share"]){
//微博分享成功
if (response.statusCode == WeiboSDKResponseStatusCodeSuccess){
[[NSNotificationCenter defaultCenter] postNotificationName:kSinaShare object:nil userInfo:@{@"state": @(GMShareResponseStateSuccess)}];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:kSinaShare object:nil userInfo:@{@"state": @(GMShareResponseStateFail)}];
}
}
}else if ([response isKindOfClass:WBAuthorizeResponse.class]){
accessToken = [(WBAuthorizeResponse *)response accessToken];
userId = [(WBAuthorizeResponse *)response userID];
[self requstWeiBoUseInfoByAccessToken:accessToken userId:userId];
}
}
- (void)requstWeiBoUseInfoByAccessToken: (NSString *)accessToken userId: (NSString *)userId{
//授权成功后获取用户信息
NSDictionary * dic = @{};
if ([accessToken isNonEmpty] && [userId isNonEmpty]) {
dic = @{@"access_token": accessToken, @"uid": userId};
//发送请求 获取各种数据 对应的URL和请求方式heepMethod是不同的 详细的参考微博开放平台
[WBHttpRequest requestWithURL:@"https://api.weibo.com/2/users/show.json" httpMethod:@"GET" params:dic delegate:[GMThirdPartyOauthManager shareInstance] withTag:@"gengmei"];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:kSinaLogin object:nil userInfo:@{@"state": @(GMShareResponseStateCancel)}];
}
}
//微博授权成功的回调
-(void)request:(WBHttpRequest *)request didFinishLoadingWithResult:(NSString *)result{
NSData *data=[result dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSString *location = dic[@"location"];
NSArray * array = [location componentsSeparatedByString:@" "];
if (array.count == 1){
_user.provice = array.firstObject;
} else if(array.count == 2){
_user.provice = array.firstObject;
_user.city = array.lastObject;
}
_user.uid = [NSString stringWithFormat:@"%@",dic[@"id"]];
_user.nickname = dic[@"screen_name"];
_user.icon = dic[@"profile_image_url"];
_user.platformType = GMSharePlatformSinaWeibo;
[[NSNotificationCenter defaultCenter] postNotificationName:kSinaLogin object:_user userInfo:@{@"state": @(GMShareResponseStateSuccess)}];
}
//微博授权失败的回调
- (void)request:(WBHttpRequest *)request didFailWithError:(NSError *)error{
[[NSNotificationCenter defaultCenter] postNotificationName:kSinaLogin object:nil userInfo:@{@"state": @(GMShareResponseStateFail)}];
}
#pragma mark -- TencentSessionDelegate
- (void)tencentDidLogin {
if (![self.tencentOAuth.accessToken isNonEmpty]){
NSArray* permissions = [NSArray arrayWithObjects:
kOPEN_PERMISSION_GET_USER_INFO,
kOPEN_PERMISSION_GET_SIMPLE_USER_INFO,
nil];
[self.tencentOAuth reauthorizeWithPermissions:permissions];
} else {
[self.tencentOAuth getUserInfo];
}
}
- (BOOL)tencentNeedPerformReAuth:(TencentOAuth *)tencentOAuth{
return YES;
}
//点击返回的回调
- (void)tencentDidNotLogin:(BOOL)cancelled{
[[NSNotificationCenter defaultCenter] postNotificationName:kQQLogin object:nil userInfo:@{@"state": @(GMShareResponseStateCancel)}];
}
- (void)tencentDidNotNetWork{
[[NSNotificationCenter defaultCenter] postNotificationName:kQQLogin object:nil userInfo:@{@"state": @(GMShareResponseStateFail)}];
}
- (BOOL)tencentNeedPerformIncrAuth:(TencentOAuth *)tencentOAuth withPermissions:(NSArray *)permissions{
[tencentOAuth incrAuthWithPermissions:permissions];
return NO;
}
- (void)getUserInfoResponse:(APIResponse *)response {
if (response && response.retCode == URLREQUEST_SUCCEED) {
_user.uid = self.tencentOAuth.getUserOpenID;
_user.nickname = response.jsonResponse[@"nickname"];
_user.icon = response.jsonResponse[@"figureurl_2"];
_user.city = response.jsonResponse[@"city"];
_user.provice = response.jsonResponse[@"province"];
_user.platformType = GMSharePlatformQQ;
[[NSNotificationCenter defaultCenter] postNotificationName:kQQLogin object:_user userInfo:@{@"state": @(GMShareResponseStateSuccess)}];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:kQQLogin object:nil userInfo:@{@"state": @(GMShareResponseStateFail)}];
}
}
# pragma mark -- WXApiDelegate
- (void)onResp:(BaseResp *)resp {
if([resp isKindOfClass:[SendAuthResp class]]){
// 微信请求App提供内容, 需要app提供内容后使用sendRsp返回
SendAuthResp *authResp = (SendAuthResp *)resp;
if (resp.errCode == 0) {
[self requestWechatAccessTokenWithCode:authResp.code];
} else if(resp.errCode == -2){
[[NSNotificationCenter defaultCenter] postNotificationName:kWechatLogin object:_user userInfo:@{@"state": @(GMShareResponseStateCancel)}];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:kWechatLogin object:_user userInfo:@{@"state": @(GMShareResponseStateFail)}];
}
} else if ([resp isKindOfClass:[SendMessageToWXResp class]]) {
if (resp.errCode == 0) {
[[NSNotificationCenter defaultCenter] postNotificationName:kWechatShare object:nil userInfo:@{@"state": @(GMShareResponseStateSuccess)}];
} else if(resp.errCode == -2){
[[NSNotificationCenter defaultCenter] postNotificationName:kWechatShare object:nil userInfo:@{@"state": @(GMShareResponseStateCancel)}];
}
}
}
//微信的授权需要每次通过refreshToken去校验access_token是否失效
- (void)requestWechatAccessTokenWithCode:(NSString *)code {
NSString *accessUrl = [NSString stringWithFormat:@"%@/oauth2/access_token?appid=%@&secret=%@&code=%@&grant_type=authorization_code",WEICHAT_BASE_URL, WEIXIN_APPID, WEIXIN_SECRET, code];
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:accessUrl] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSString *refreshToken = responseObject[@"refresh_token"];
[self refreshAccessTokenByRefreshToken:refreshToken];
} else {
NSLog(@"获取accessToken出错");
}
}] resume];
}
//刷新access_token有效期
- (void)refreshAccessTokenByRefreshToken:(NSString *)refreshToken{
NSString *refreshUrl = [NSString stringWithFormat:@"%@/oauth2/refresh_token?appid=%@&grant_type=refresh_token&refresh_token=%@",WEICHAT_BASE_URL, WEIXIN_APPID, refreshToken];
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:refreshUrl] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSString *accessToken = responseObject[@"access_token"];
NSString *openId = responseObject[@"openid"];
[[GMThirdPartyOauthManager shareInstance] requestWechatUserInfoByAccessToken:accessToken openId:openId];
} else {
NSLog(@"刷新access_token时出错,请重新授权");
}
}] resume];
}
- (void)requestWechatUserInfoByAccessToken:(NSString *)accessToken openId:(NSString *)openId{
NSString *userUrlStr = [NSString stringWithFormat:@"%@/userinfo?access_token=%@&openid=%@", WEICHAT_BASE_URL, accessToken, openId];
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:userUrlStr] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
self.user.uid = responseObject[@"openid"];
self.user.nickname = responseObject[@"nickname"];
self.user.icon = responseObject[@"headimgurl"];
self.user.city = responseObject[@"city"];
self.user.provice = responseObject[@"province"];
self.user.unionid = responseObject[@"unionid"];
self.user.platformType = GMSharePlatformWechat;
[[NSNotificationCenter defaultCenter] postNotificationName:kWechatLogin object:_user userInfo:@{@"state": @(GMShareResponseStateSuccess)}];
});
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:kWechatLogin object:_user userInfo:@{@"state": @(GMShareResponseStateFail)}];
}
}] resume];
}
网友评论