文章涉及的demo在Github LQThirdParty, 歡迎Star | Fork
關(guān)于第三方登錄/分享的接入, 很多時候使用的是友盟或者ShareSDK; 但并不是每次都想使用這些第三方的服務(wù)的, 這里作者整理了微信, QQ, 新浪微博原生第三方的接入:
[Swift]原生第三方接入: 微信篇--集成/登錄/分享/支付
[Swift]原生第三方接入: QQ篇--集成/登錄/分享
[Swift]原生第三方接入: 新浪微博篇--集成/登錄/分享
一. 集成
1.1 新建應(yīng)用
首先, 想要使用騰訊相關(guān)的功能, 您必須注冊成為騰訊認(rèn)證的開發(fā)者, 并且在騰訊開放平臺創(chuàng)建了應(yīng)用, 即已經(jīng)獲取到了相應(yīng)的 APPID 和 APPKEY.
1.2. 集成SDK
個人感覺騰訊的文檔不太友好, 先給出官方文檔地址和官方SDK下載地址下載最新的文檔及SDK. 將下載后的SDK中的以下兩個文件添加到項目工程目錄:
TencentOpenApi_IOS_Bundle.bundle
TencentOpenAPI.framework
添加系統(tǒng)依賴庫
到Build Phases -> Link Binary With Libraries
, 添加以下系統(tǒng)庫 :
- Security.framework
- SystemConfiguration.framework
- CoreGraphics.Framework
- CoreTelephony.framework
- libiconv.dylib
- libsqlite3.dylib
- libstdc++.dylib
- libz.dylib
后面四個下新版Xcode中為:
- libiconv.tbd
- libsqlite3.tbd
- libstdc++.tbd
- libz.tbd
添加 TencentOpenApi_IOS_Bundle.bundle
然后來到Build Phases -> Copy Bundle Resources
將 TencentOpenApi_IOS_Bundle.bundle 添加進(jìn)來(一般會自動添加到這里, 看下有沒有即可, 沒有的話, 點擊 + 添加)
添加 -fobjc-arc
來到Build Settings -> Other Linker Flags
添加 -fobjc-arc
PS: 如果這里已有其他內(nèi)容, 加個空格粘貼進(jìn)去即可, 個人嘗試可行.
添加URL Scheme
來到Info-> URL Types
, 點擊左下角的 + 新加一個Scheme
格式: tencent+AppID
例如你的AppID為: 123456789
則你的Scheme為: tencent123456789
適配iOS 9+ , 添加Scheme白名單
- 方式一
在Info.plist文件內(nèi)新加字段: LSApplicationQueriesSchemes, 類型為Array(數(shù)組)
然后添加內(nèi)容, 類型為String(字符串)
QQ需要添加以下字段:
- mqqOpensdkSSoLogin,
- mqqopensdkapiV2,
- mqqopensdkapiV3,
- wtloginmqq2,
- mqq,
- mqqapi
- mqqopensdkdataline
QZONE 需要添加:
- mqzoneopensdk,
- mqzoneopensdkapi,
- mqzoneopensdkapi19,
- mqzoneopensdkapiV2,
- mqqOpensdkSSoLogin,
- mqqopensdkapiV2,
- mqqopensdkapiV3,
- wtloginmqq2,
- mqqapi,
- mqqwpa,
- mqzone,
- mqq
- mqqopensdkapiV4
- mqqopensdkdataline
如果同時需要QQ和Qzone, 只需要添加Qzone即可;
- 方式二
或者, 在Info.plist文件右鍵, Open as... -> Source Code, 可打開文件, 進(jìn)行編輯, 在倒數(shù)第三行(即: </dict> 標(biāo)簽的上面)的空白處添加以下代碼:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>mqzoneopensdk</string>
<string>mqzoneopensdkapi</string>
<string>mqzoneopensdkapi19</string>
<string>mqzoneopensdkapiV2</string>
<string>mqqOpensdkSSoLogin</string>
<string>mqqopensdkapiV2</string>
<string>mqqopensdkapiV3</string>
<string>wtloginmqq2</string>
<string>mqqapi</string>
<string>mqqwpa</string>
<string>mqzone</string>
<string>mqq</string>
<string>mqqopensdkapiV4</string>
<string> mqqopensdkdataline </string>
</array>
如果還有其他平臺的白名單需要添加, 例如微信, 新浪微博, 只需要在<array></array>標(biāo)簽內(nèi)添加對應(yīng)的字段即可;
PS: Info.plist文件顯示為Source Code后, 如果還想顯示原來的列表格式, 可以: 郵件 -> Open as.. -> Property List 即可.
適配iOS 9+, 網(wǎng)絡(luò)請求
-
方式一: 暫時回退到HTTP請求
在Info.plist文件中添加字段: NSAppTransportSecurity, 類型為字典;
然后添加一個Key:NSAllowsArbitraryLoads, 類型為Boolean, 值為 YES
或者以Source Code 打開Info.plist文件, 空白處添加以下代碼:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
- 方式二: 設(shè)置域
在項目的info.plist中添加一個Key:NSAppTransportSecurity碳胳,類型為字典類型。
然后給它添加一個值: NSExceptionDomains决瞳,類型為字典類型;
把需要的支持的域添加給NSExceptionDomains
其中域作為Key左权,類型為字典類型皮胡。
每個域下面需要設(shè)置3個屬性:
NSIncludesSubdomains、
NSExceptionRequiresForwardSecrecy赏迟、NSExceptionAllowsInsecureHTTPLoads屡贺。
均為Boolean類型,值分別為YES、NO甩栈、YES
QQ需要設(shè)置的域為:
或者以Source Code 打開Info.plist文件, 空白處添加以下代碼:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>qq.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
PS: 這種方式需要對每個要以HTTP方式訪問的域名進(jìn)行設(shè)置, 比較麻煩, 建議使用第一種方式.
到此, 集成及適配結(jié)束...
PS: 在使用相關(guān)API的時候, 需要新建橋接頭文件, 或者在已有橋接頭文件內(nèi)引用其頭文件:
#import <TencentOpenAPI/TencentOAuth.h>
#import <TencentOpenAPI/QQApiInterfaceObject.h>
#import <TencentOpenAPI/QQApiInterface.h>
PS: 新的SDK在接入后會報一串這個錯誤:
Redefinition of 'TencentAuthorizeState'
...
只需要修改頭文件中的這個文件內(nèi)容:module.modulemap
framework module TencentOpenAPI {
header "QQApiInterface.h"
header "QQApiInterfaceObject.h"
header "TencentOAuth.h"
header "TencentOpenApiUmbrellaHeader.h"
header "sdkdef.h"
export *
}
clean之后泻仙,重新編譯即可!
二. 登錄
在AppDelegate.swift
中注冊app:
QQ在注冊App的時候, 和其他的不同, 是要創(chuàng)建一個TencentOAuth對象, 來發(fā)起授權(quán)申請:
var tencentAuth: TencentOAuth!
self. tencentAuth = TencentOAuth(appId: qqAppID, andDelegate: self)
在方法 func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool
中添加回調(diào):
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
let urlKey: String = options[UIApplicationOpenURLOptionsKey.sourceApplication] as! String
if urlKey == "com.tencent.mqq" {
// QQ 的回調(diào)
return TencentOAuth.handleOpen(url)
}
return true
}
然后, 實現(xiàn)其代理方法:
func tencentDidLogin() {
// 登錄成功后要調(diào)用一下這個方法, 才能獲取到個人信息
self.tencentAuth.getUserInfo()
}
func tencentDidNotNetWork() {
// 網(wǎng)絡(luò)異常
}
func tencentDidNotLogin(_ cancelled: Bool) {
}
func getUserInfoResponse(_ response: APIResponse!) {
// 獲取個人信息
if response.retCode == 0 {
if let res = response.jsonResponse {
if let uid = self.tencentAuth.getUserOpenID() {
// 獲取uid
}
if let name = res["nickname"] {
// 獲取nickname
}
if let sex = res["gender"] {
// 獲取性別
}
if let img = res["figureurl_qq_2"] {
// 獲取頭像
}
}
} else {
// 獲取授權(quán)信息異常
}
}
最后在需要發(fā)起登錄的地方添加以下代碼來發(fā)起登錄申請:
let appDel = UIApplication.shared.delegate as! AppDelegate
// 需要獲取的用戶信息
let permissions = [kOPEN_PERMISSION_GET_USER_INFO, kOPEN_PERMISSION_GET_SIMPLE_USER_INFO]
appDel.tencentAuth.authorize(permissions)
到此, 一個完整的QQ登錄授權(quán)流程就完成了,并成功的獲取到了QQ用戶的相關(guān)信息, 即登錄成功.
上面我是在AppDelegate.swift方法內(nèi)處理的回調(diào), 我們可以完全寫在其他的類里面, 只需要將其代理對象設(shè)置為需要處理回調(diào)的對象即可.
三. 分享
一直在吐槽騰訊的開放平臺, 找個文檔真心不容易, 繞了一些彎路, 最后還是百度搜到了文檔地址, 確實很尷尬. 雖然找到了官方文檔說明, 但是不是特別詳細(xì), 可以下載其詳細(xì)文檔來參考設(shè)置.
PS: 在配置工程的時候需要注意, 如果需要兼容舊版本的手機(jī)QQ, 需要額外添加一個 URL Scheme : QQ + 十六進(jìn)制的AppID ,不足八位的在首部補(bǔ)0 ; 例如你的AppID為: 123456 , 其十六進(jìn)制為: 1E240, 則, URL Scheme 為: QQ0001E240
不過, 現(xiàn)在的QQ版本都比較高了, 至于低于哪個版本需要兼容, 官方文檔沒有提, 如果你的開發(fā)中適配低版本QQ時不能成功分享, 不妨添加試試.
如果是單獨集成了分享, 在注冊APP的時候依然是使用下面這個方法:
TencentOAuth(appId: "appid", andDelegate: nil)
如果沒有登錄, 這里代理傳nil即可; 如果有登錄, 按登錄的設(shè)置即可;
在其代理類中實現(xiàn)代理( QQApiInterfaceDelegate )方法:
func onReq(_ req: QQBaseReq!) {
}
func onResp(_ resp: QQBaseResp!) {
if resp is SendMessageToQQResp {
let rs = resp as! SendMessageToQQResp
if rs.type == 2 {
// QQ分享返回的回調(diào)
if rs.result == "0" {
// 分享成功
print("分享成功")
} else {
print("分享失敗")
}
}
}
}
func isOnlineResponse(_ response: [AnyHashable : Any]!) {
}
雖然我們只用在代理方法 func onResp(_ resp: QQBaseResp!) 中處理分享的回調(diào), 但其他兩個方法也是要實現(xiàn).
PS: 這里需要注意, 如果項目中集成了微信, QQ的代理和微信的代理需要分開處理, 不能使用同一個對象, 否則會編譯報錯:
Method 'onReq' with Objective-C selector 'onReq:' conflicts with previous declaration with the same Objective-C selector
然后在 func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool 方法中添加回調(diào):
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
return QQApiInterface.handleOpen(url, delegate: LDShareUnit.shared)
}
最后, 就是在需要分享的地方發(fā)起分享了, 在這里我遇到一個坑.
分享文本
以分享文本為例. 開始我的代碼是這樣的:
let textObj = QQApiTextObject()
textObj.text = "這是分享到QQ的一段文字"
textObj.title = "這是分享到QQ的標(biāo)題"
textObj.description = "一段描述"
textObj.shareDestType = ShareDestTypeQQ // 分享到QQ 還是TIM, 必須指定
let req = SendMessageToQQReq()
req.message = textObj
let code = QQApiInterface.send(req)
print(code)
這樣寫, 一直無法吊起QQ客戶端, 打印返回的code值, 一直是: QQApiSendResultCode(rawValue: -1), 發(fā)送失敗; 查了很多資料, 嘗試了各種情況, 都沒能解決. 最后擱置了些時間, 一時心血來潮, 換了個初始化的方法, 改成如下這樣:
let textObj = QQApiTextObject(text: "這是分享到QQ的一段文字")
// textObj.text = "這是分享到QQ的一段文字"
textObj?.title = "這是分享到QQ的標(biāo)題"
textObj?.description = "一段描述"
textObj?.shareDestType = ShareDestTypeQQ // 分享到QQ 還是TIM, 必須指定
let req = SendMessageToQQReq(content: textObj)
req?.message = textObj
// QQApiInterface.openQQ()
let code = QQApiInterface.send(req)
print(code)
奇跡竟然發(fā)生了, 就這樣, 成功的吊起QQ客戶端, 并分享出去了.......
上面的title和description不需要設(shè)置, 設(shè)置了也看不到.
關(guān)于 shareDestType 參數(shù), 官方文檔指出必須設(shè)置, 否則無法正常工作, 我發(fā)現(xiàn)即使不設(shè)置, 默認(rèn)也是吊起QQ發(fā)起分享的.
這算是一個比較大, 也是比較坑的一個大坑了, 花了很多時間, 做了一些無用功, 這也算是其API的一個不太友好的地方吧.
PS: 這里對 QQApiObject 的一個參數(shù) cflag 做一下說明, 這是設(shè)置要分享的內(nèi)容到什么地方, 空間, 收藏, 聊天, 電腦等
// QQApiObject control flags
enum
{
kQQAPICtrlFlagQZoneShareOnStart = 0x01,
kQQAPICtrlFlagQZoneShareForbid = 0x02,
kQQAPICtrlFlagQQShare = 0x04,
kQQAPICtrlFlagQQShareFavorites = 0x08, //收藏
kQQAPICtrlFlagQQShareDataline = 0x10, //數(shù)據(jù)線
};
- kQQAPICtrlFlagQQShareDataline
只分享文字/圖片等到電腦 - kQQAPICtrlFlagQQShareFavorites
只分享到文字/圖片等到收藏 - kQQAPICtrlFlagQQShare
分享到QQ, 可以選擇到聯(lián)系人/ 收藏/ 電腦/ 空間等 - kQQAPICtrlFlagQZoneShareForbid
禁止分享到QQ空間 - kQQAPICtrlFlagQZoneShareOnStart
只分享到QQ空間
如果想要設(shè)置不同的分享目標(biāo), 可以設(shè)置這個參數(shù).
分享圖片
- 單圖
func shareImageToQQ() {
// 原圖 最大5M
let img = UIImage(named: "1.jpg")
let data = UIImageJPEGRepresentation(img!, 0.8)
// 預(yù)覽圖 最大 1M
let thumb = UIImage(named: "qq")
let thData = UIImagePNGRepresentation(thumb!)
let imgObj = QQApiImageObject(data: data, previewImageData: thData, title: "分享的一張圖片", description: "分享圖片的描述")
let req = SendMessageToQQReq(content: imgObj)
// 分享到QQ
QQApiInterface.send(req)
// 分享到Qzone
// QQApiInterface.sendReq(toQZone: req)
}
如果要分享到QZone, 可以設(shè)置imgObj?.cflag = UInt64(kQQAPICtrlFlagQZoneShareOnStart), 也可以使用QQApiInterface.sendReq(toQZone: req)
- 多圖
多圖只能分享到 QQ收藏
func shareImagesToQQ() {
// 多圖不支持分享到QQ, 如果設(shè)置, 默認(rèn)分享第一張
// k可以分享多圖到QQ收藏
let img = UIImage(named: "1.jpg")
let data = UIImageJPEGRepresentation(img!, 0.8)
let img1 = UIImage(named: "10633861_160536558132_2.jpg")
let data1 = UIImageJPEGRepresentation(img1!, 0.8)
let thumb = UIImage(named: "qq")
let thData = UIImagePNGRepresentation(thumb!)
let imgObj = QQApiImageObject(data: data, previewImageData: thData, title: "分享多個圖片", description: "描述", imageDataArray: [data!, data1!])
// 設(shè)置分享目標(biāo)為QQ收藏
imgObj?.cflag = UInt64(kQQAPICtrlFlagQQShareFavorites)
let req = SendMessageToQQReq(content: imgObj)
QQApiInterface.send(req)
}
PS: 這里在設(shè)置分享到QQ收藏的時候, 遇到一個錯誤提示:
-canOpenURL: failed for URL: "mqqopensdkdataline://" - error: "This app is not allowed to query for scheme mqqopensdkdataline"
這是因為之前配置的工程白名單的時候沒有添加mqqopensdkdataline , 在第一部分的 適配iOS 9+ , 添加Scheme白名單 中添加mqqopensdkdataline即可;
分享新聞鏈接
新聞可以分享到QQ, 也可以只分享到QZone
func shareNewsToQQ() {
let url = URL(string: "http://www.reibang.com/u/2846c3d3a974")
let preURL = URL(string: "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496830989997&di=5ca7528be6f496c6500a436c8775a67d&imgtype=0&src=http%3A%2F%2Fwww.pp3.cn%2Fuploads%2F201502%2F2015021111.jpg")
let obj = QQApiNewsObject(url: url!, title: "關(guān)注作者流火緋瞳", description: "這是一個coder, 不是一個美女", previewImageURL: preURL!, targetContentType: QQApiURLTargetTypeNews)
let req = SendMessageToQQReq(content: obj)
// 分享到QQ
// QQApiInterface.send(req)
// 分享到QZone
QQApiInterface.sendReq(toQZone: req)
}
在創(chuàng)建新聞實例對象 QQApiNewsObject 的時候, 可以使用上面的方法, 也可以使用:
QQApiNewsObject(url: URL!, title: String!, description: String!, previewImageData: Data!, targetContentType: QQApiURLTargetType)
只是參數(shù)類型不同, 含義和上面的方法是一致的;
分享音樂
音樂可以分享到QQ和QZone
func shareMusicToQQ() {
let url = URL(string: "http://y.qq.com/i/song.html?songid=432451&source=mobileQQ%23wechat_redirect")
let preUrl = URL(string: "http://imgcache.qq.com/music/photo/mid_album_300/V/E/000J1pJ50cDCVE.jpg")
let obj = QQApiAudioObject(url: url!, title: "歌曲名:不要說話", description: "專輯名:不想放手歌手名:陳奕迅", previewImageURL: preUrl!, targetContentType: QQApiURLTargetTypeVideo)
let req = SendMessageToQQReq(content: obj)
// 分享到QQ
// QQApiInterface.send(req)
// 分享到QZone
QQApiInterface.sendReq(toQZone: req)
}
這里在創(chuàng)建 QQApiAudioObject 實例對象的時候, 可以使用上面的方法, 也可以使用下面的方法, 只是參數(shù)類型不同, 其含義一致:
let obj = QQApiAudioObject(url: URL!, title: String!, description: String!, previewImageData: Data!, targetContentType: QQApiURLTargetType)
分享視頻
視頻可以分享到QQ和QZone
func shareVideoToQQ() {
let url = URL(string: "視頻URL地址")
let preURL = URL(string: "視頻預(yù)覽圖片URL地址")
let obj = QQApiVideoObject(url: url!, title: "分享的視頻名稱", description: "視頻內(nèi)容描述", previewImageURL: preURL!, targetContentType: QQApiURLTargetTypeVideo)
let req = SendMessageToQQReq(content: obj)
// 分享到QQ
// QQApiInterface.send(req)
//分享到QZone
QQApiInterface.sendReq(toQZone: req)
}
這里在創(chuàng)建QQApiVideoObject實例的時候, 可以使用上面的方法, 也可以使用下面的方法, 只是參數(shù)類型不同, 其含義一致:
let obj = QQApiVideoObject(url: URL!, title: String!, description: String!, previewImageData: Data!, targetContentType: QQApiURLTargetType
(完)
以上便是 QQ登錄分享的所有內(nèi)容, 如有不正確的地方, 還請評論指出, 或者私信;
文章涉及的demo在Github LQThirdParty, 歡迎Star | Fork