導(dǎo)語(yǔ):
第三方登錄和分享是現(xiàn)在App的「主流功能」,不管友盟,還是ShareSDK,都有很好的集成文檔,Please feel free to check!至于寫這篇指南的初衷是剛好公司最近做Standard Module,也剛好需要集成這一塊,還得做成友盟那樣以供使用。(心累,迫于老大的淫威之下
QQ SDK官方文檔
實(shí)現(xiàn)QQ登錄
一、準(zhǔn)備工作
- 在騰訊開(kāi)放平臺(tái),注冊(cè)成為開(kāi)發(fā)者。
- 創(chuàng)建應(yīng)用,申請(qǐng)你的appid和appkey。
!!!Ps:在準(zhǔn)備工作的時(shí)候,可能會(huì)遇到一個(gè)坑:QQ互聯(lián)開(kāi)放平臺(tái)和騰訊開(kāi)放平臺(tái)是相互獨(dú)立的,所以 若已在QQ互聯(lián)創(chuàng)建過(guò)應(yīng)用,可在創(chuàng)建應(yīng)用時(shí)選擇關(guān)聯(lián)QQ互聯(lián),輸入在QQ互聯(lián)創(chuàng)建的網(wǎng)頁(yè)應(yīng)用APPID、APPKEY即可完成關(guān)聯(lián)。(兩個(gè)平臺(tái)做一樣的事,鵝廠真心錢多

appid和appkey的用途
- appid:應(yīng)用的唯一標(biāo)識(shí)。在OAuth2.0認(rèn)證過(guò)程中,appid的值即為oauth_consumer_key的值。
- appkey:appid對(duì)應(yīng)的密鑰,訪問(wèn)用戶資源時(shí)用來(lái)驗(yàn)證應(yīng)用的合法性。在OAuth2.0認(rèn)證過(guò)程中,appkey的值即為oauth_consumer_secret的值。
二、集成SDK
下載好完整包,如圖導(dǎo)入工程需要的文件。

- TencentOpenAPI.framework打包了iOS SDK的頭文件定義和具體實(shí)現(xiàn)
- TencentOpenApi_iOS_Bundle.bundle 打包了iOS SDK需要的資源文件
三、配置工程
1. 添加SDK依賴的系統(tǒng)庫(kù)文件
Security.framework
SystemConfiguration.framework
CoreTelephony.framework
CoreGraphics.Framework
libiconv.tbd
libsqlite3.tbd
libstdc++.tbd
libz.tbd
2. 修改必要的工程配置屬性
在工程配置中的Build Settings一欄中找到Linking配置區(qū),給Other Linker Flags配置項(xiàng)添加屬性值-fobjc-arc

3. 修改Info.plist文件
在XCode中,選中TARGETS一欄,在Info標(biāo)簽欄中找到URL Types,添加一條新的URL scheme。(必須填寫
Identifier: tencentopenapi
URL Schemes: tencent + "appid"
想要實(shí)現(xiàn)應(yīng)用間跳轉(zhuǎn),而不是打開(kāi)一個(gè)登陸網(wǎng)頁(yè),在Info.plist中添加LSApplicationQueriesSchemes:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>mqqapi</string>
<string>mqq</string>
<string>mqqOpensdkSSoLogin</string>
<string>mqqconnect</string>
<string>mqqopensdkdataline</string>
<string>mqqopensdkgrouptribeshare</string>
<string>mqqopensdkfriend</string>
<string>mqqopensdkapi</string>
<string>mqqopensdkapiV2</string>
<string>mqqopensdkapiV3</string>
<string>mqzoneopensdk</string>
<string>mqqopensdkapiV3</string>
<string>mqqopensdkapiV3</string>
<string>mqzone</string>
<string>mqzonev2</string>
<string>mqzoneshare</string>
<string>wtloginqzone</string>
<string>mqzonewx</string>
<string>mqzoneopensdkapiV2</string>
<string>mqzoneopensdkapi19</string>
<string>mqzoneopensdkapi</string>
<string>mqzoneopensdk</string>
</array>
四、代碼實(shí)現(xiàn)
1.AppDelegate
在AppDelegate中添加頭文件,并重寫AppDelegate的handleOpenURL和openURL方法
-
iOS9之前,分別重寫
handleOpenURL && openURL方法//handleOpenURL(ios10已棄用) NS_DEPRECATED_IOS(2_0, 9_0, "Please use application:openURL:options:") - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{ [TencentOAuth HandleOpenURL:url]; return YES; } //openURL(iOS10已棄用) NS_DEPRECATED_IOS(4_2, 9_0, "Please use application:openURL:options:") - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{ [TencentOAuth HandleOpenURL:url]; return YES; } -
iOS9之后,
handleOpenURL && openURL合成為同一個(gè)方法//handleOpenURL && openURL - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options{ [TencentOAuth HandleOpenURL:url]; return YES; }
2.登錄控制器實(shí)現(xiàn)對(duì)應(yīng)的代理事件
在LoginViewController添加對(duì)應(yīng)的頭文件,添加QQ Login點(diǎn)擊事件,實(shí)現(xiàn)TencentSessionDelegate的對(duì)應(yīng)delegate事件。
#import "LoginViewController.h"
#import <TencentOpenAPI/TencentOAuth.h>
@interface LoginViewController ()<TencentSessionDelegate>
@property (nonatomic, strong) TencentOAuth* tencentOAuth;
@end
@implementation LoginViewController
- (void)loginBtnClicked{
self.tencentOAuth = [[TencentOAuth alloc] initWithAppId:@"你申請(qǐng)的appID" andDelegate:self];
//在這個(gè)數(shù)組里,可以添加你想要用戶授權(quán)的信息的相關(guān)字段
//注意,若是授權(quán)太多信息, 用戶可能會(huì)拒絕授權(quán)
NSArray *permissions = [NSArray arrayWithObjects:
@"get_user_info",
@"get_simple_userinfo",
@"add_t",
nil];
//授權(quán)類型
[self.tencentOAuth setAuthShareType:AuthShareType_QQ];
//調(diào)用SDK登錄
[self.tencentOAuth authorize:permissions inSafari:false];
}
- (void)tencentDidNotNetWork{
NSLog(@"網(wǎng)絡(luò)連接失敗");
}
- (void)tencentDidLogin{
if (self.tencentOAuth.accessToken && 0 != [self.tencentOAuth.accessToken length]){
//accessToken有效期3個(gè)月,想要獲取用戶信息,必須調(diào)用getUserInfo
NSLog(@"記錄登錄用戶的OpenID、Token以及過(guò)期時(shí)間");
[self.tencentOAuth getUserInfo];
}else{
NSLog(@"登錄不成功 沒(méi)有獲取accesstoken");
}
}
- (void)tencentDidNotLogin:(BOOL)cancelled{
if (cancelled){
NSLog(@"用戶取消登錄");
}else{
NSLog(@"登錄失敗");
}
}
3.獲取用戶信息
當(dāng)DidLogin調(diào)用getUserInfo,會(huì)有此回調(diào)返回userInfo
- (void)getUserInfoResponse:(APIResponse *)response{
if (response && response.retCode == URLREQUEST_SUCCEED) {
NSDictionary* userInfo = [response jsonResponse];
NSLog(@"%@",userInfo);
}else{
NSLog(@"QQ auth fail ,getUserInfoResponse:%d", response.detailRetCode);
}
}
4.登錄篇注意事項(xiàng)
?當(dāng)未調(diào)起原生客戶端授權(quán),只彈出網(wǎng)頁(yè),檢查URL scheme設(shè)置 && 是否添加
LSApplicationQueriesSchemes到Info.plist-
當(dāng)Xcode提示“未選擇授權(quán)類型”,可添加:
[self.tencentOAuth setAuthShareType:AuthShareType_QQ];
分割線,接下來(lái)講講QQ分享
實(shí)現(xiàn)QQ分享
步驟同上一、二、三
代碼實(shí)現(xiàn)
1.在AppDelegate中初始化OAuth
@interface AppDelegate ()<QQApiInterfaceDelegate>
@property (nonatomic, strong) TencentOAuth* tencentOAuth;
@end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// 根據(jù)自己的AppID初始化OAuth
self.tencentOAuth = [[TencentOAuth alloc] initWithAppId:@"QQAppID" andDelegate:nil];
return YES;
}
2.在AppDelegate中實(shí)現(xiàn)回調(diào)事件
此回調(diào)事件,需要在AppDelegate里添加頭文件<TencentOpenAPI/QQApiInterface.h>,重寫handleOpenURL && openURL方法
-
添加頭文件
#import <TencentOpenAPI/QQApiInterface.h> -
重寫
handleOpenURL方法//iOS9之前,分別重寫`handleOpenURL && openURL`方法 //handleOpenURL(ios10已棄用) NS_DEPRECATED_IOS(2_0, 9_0, "Please use application:openURL:options:") - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{ if ([url.scheme isEqualToString:[NSString stringWithFormat:@"tencent%@",QQAppID]]) { return [QQApiInterface handleOpenURL:url delegate:self]; } else{ return YES; } } //iOS9之后,`handleOpenURL && openURL` 合成為同一個(gè)方法 //handleOpenURL && openURL - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options{ if ([url.scheme isEqualToString:[NSString stringWithFormat:@"tencent%@",QQAppID]]) { return [QQApiInterface handleOpenURL:url delegate:self]; } else{ return YES; } } -
處理回調(diào)事件
@interface AppDelegate ()<QQApiInterfaceDelegate> - (void)onReq:(QQBaseReq *)req{ NSLog(@"處理來(lái)至QQ的請(qǐng)求"); } - (void)onResp:(QQBaseResp *)resp{ NSLog(@"處理來(lái)至QQ的響應(yīng)"); NSLog(@"response") switch (resp.type) { case ESENDMESSAGETOQQRESPTYPE: { SendMessageToQQResp* sendResp = (SendMessageToQQResp*)resp; if ([sendResp.result isEqualToString:@"0"]) { UIAlertController* vc = [UIAlertController alertControllerWithTitle:@"成功" message:@"QQ分享成功" preferredStyle:UIAlertControllerStyleAlert]; [vc addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; [self.window.rootViewController presentViewController:vc animated:YES completion:nil]; } else { UIAlertController* vc = [UIAlertController alertControllerWithTitle:@"失敗" message:@"QQ分享失敗" preferredStyle:UIAlertControllerStyleAlert]; [vc addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; [self.window.rootViewController presentViewController:vc animated:YES completion:nil]; } break; } default: { break; } } } - (void)isOnlineResponse:(NSDictionary *)response{ NSLog(@"處理QQ在線狀態(tài)的回調(diào)"); NSLog(@"response : %@",response); }
3.在ShareViewController中導(dǎo)入對(duì)應(yīng)的頭文件
#import <TencentOpenAPI/TencentOAuth.h>
#import <TencentOpenAPI/TencentMessageObject.h>
#import <TencentOpenAPI/TencentApiInterface.h>
#import <TencentOpenAPI/QQApiInterface.h>
#import <TencentOpenAPI/QQApiInterfaceObject.h>
4.在ShareViewController實(shí)現(xiàn)分享事件示例代碼
-
文本
- (void)shareText { if (![TencentOAuth iphoneQQInstalled]) { UIAlertController* vc = [UIAlertController alertControllerWithTitle:@"你還沒(méi)有安裝QQ客戶端" message:nil preferredStyle:UIAlertControllerStyleAlert]; [vc addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; [self presentViewController:vc animated:YES completion:nil]; } else{ QQApiTextObject* object = [QQApiTextObject objectWithText:@"分享的內(nèi)容"]; [object setShareDestType:ShareDestTypeQQ]; SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent:object]; QQApiSendResultCode code = [QQApiInterface sendReq:req]; NSLog(@"result code : %d",code); } } -
圖片
- (void)shareImage { if (![TencentOAuth iphoneQQInstalled]) { UIAlertController* vc = [UIAlertController alertControllerWithTitle:@"你還沒(méi)有安裝QQ客戶端" message:nil preferredStyle:UIAlertControllerStyleAlert]; [vc addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; [self presentViewController:vc animated:YES completion:nil]; } else{ NSString* filePath = [[NSBundle mainBundle] pathForResource:@"icon" ofType:@"png"]; NSData* imageData = [NSData dataWithContentsOfFile:filePath]; QQApiImageObject* obj = [QQApiImageObject objectWithData:imageData previewImageData:imageData title:@"title" description:@"github貓"]; [obj setShareDestType:ShareDestTypeQQ]; SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent:obj]; QQApiSendResultCode code = [QQApiInterface sendReq:req]; NSLog(@"result code : %d",code); } } -
新聞
- (void)shareNews { if (![TencentOAuth iphoneQQInstalled]) { UIAlertController* vc = [UIAlertController alertControllerWithTitle:@"你還沒(méi)有安裝QQ客戶端" message:nil preferredStyle:UIAlertControllerStyleAlert]; [vc addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; [self presentViewController:vc animated:YES completion:nil]; } else{ //分享跳轉(zhuǎn)URL,注意要經(jīng)過(guò)UTF8處理 NSString *url = [@"http://xxx.xxx.xxx/" stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; //分享圖預(yù)覽圖URL地址 NSString *previewImageUrl = @"preImageUrl.png"; QQApiNewsObject *newsObj = [QQApiNewsObject objectWithURL :[NSURL URLWithString:url] title: @"title" description :@"description" previewImageURL:[NSURL URLWithString:previewImageUrl]]; SendMessageToQQReq *req = [SendMessageToQQReq reqWithContent:newsObj]; //將內(nèi)容分享到qq //QQApiSendResultCode sent = [QQApiInterface sendReq:req]; //將內(nèi)容分享到qzone QQApiSendResultCode sent = [QQApiInterface SendReqToQZone:req]; } } 分享類型:
| 分享消息類型 | QQ好友 | QQ空間 | web QQ好友 | web QQ空間 |
|---|---|---|---|---|
| QQApiTextObject | ?? | ? | ? | ? |
| QQApiImageObject | ?? | ? | ? | ? |
| QQApiNewsObject | ?? | ?? | ?? | ?? |
| QQApiAudioObject | ?? | ?? | ?? | ?? |
| QQApiVideoObject | ?? | ?? | ?? | ?? |
5.分享篇注意事項(xiàng)
-
分享的回調(diào)無(wú)法執(zhí)行,請(qǐng)注意實(shí)現(xiàn)
<QQApiInterfaceDelegate>,即[QQApiInterface handleOpenURL:url delegate:self(代理對(duì)象)]; -
當(dāng)Xcode打印“result code 為 -2”時(shí)(未知的分享類型),請(qǐng)注意實(shí)現(xiàn)
setShareDestType方法,即QQApiTextObject* object = [QQApiTextObject objectWithText:@"分享的內(nèi)容"]; [object setShareDestType:ShareDestTypeQQ];
結(jié)束語(yǔ)
我在使用原生集成時(shí),遇到的這些坑都由于Tencent的文檔太久沒(méi)更新導(dǎo)致花了一段時(shí)間。最后仔細(xì)的查看SDK接口才發(fā)現(xiàn)解決方式,希望能夠給看到這篇文章的人提供到一點(diǎn)幫助。在接下來(lái)一段時(shí)間,還會(huì)繼續(xù)寫幾篇關(guān)于 微信、微博、Facebook等等的SDK集成指南。還在研究中,共勉啦。