[Swift]原生第三方接入: QQ篇--集成/登錄/分享

文章涉及的demo在Github LQThirdParty, 歡迎Star | Fork

關于第三方登錄/分享的接入, 很多時候使用的是友盟或者ShareSDK; 但并不是每次都想使用這些第三方的服務的, 這里作者整理了微信, QQ, 新浪微博原生第三方的接入:

[Swift]原生第三方接入: 微信篇--集成/登錄/分享/支付
[Swift]原生第三方接入: QQ篇--集成/登錄/分享
[Swift]原生第三方接入: 新浪微博篇--集成/登錄/分享

一. 集成

1.1 新建應用

首先, 想要使用騰訊相關的功能, 您必須注冊成為騰訊認證的開發(fā)者, 并且在騰訊開放平臺創(chuàng)建了應用, 即已經(jīng)獲取到了相應的 APPIDAPPKEY.

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 添加進來(一般會自動添加到這里, 看下有沒有即可, 沒有的話, 點擊 + 添加)

添加 -fobjc-arc

來到Build Settings -> Other Linker Flags
添加 -fobjc-arc
PS: 如果這里已有其他內容, 加個空格粘貼進去即可, 個人嘗試可行.

添加URL Scheme

來到Info-> URL Types, 點擊左下角的 + 新加一個Scheme

格式: tencent+AppID
例如你的AppID為: 123456789
則你的Scheme為: tencent123456789

添加URL Scheme
適配iOS 9+ , 添加Scheme白名單
  • 方式一

在Info.plist文件內新加字段: LSApplicationQueriesSchemes, 類型為Array(數(shù)組)
然后添加內容, 類型為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, 可打開文件, 進行編輯, 在倒數(shù)第三行(即: </dict> 標簽的上面)的空白處添加以下代碼:

<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>標簽內添加對應的字段即可;

PS: Info.plist文件顯示為Source Code后, 如果還想顯示原來的列表格式, 可以: 郵件 -> Open as.. -> Property List 即可.

適配iOS 9+, 網(wǎng)絡請求
  • 方式一: 暫時回退到HTTP請求
    在Info.plist文件中添加字段: NSAppTransportSecurity, 類型為字典;
    然后添加一個Key:NSAllowsArbitraryLoads, 類型為Boolean, 值為 YES
回退到HTTP

或者以Source Code 打開Info.plist文件, 空白處添加以下代碼:

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
  • 方式二: 設置域

在項目的info.plist中添加一個Key:NSAppTransportSecurity,類型為字典類型。
然后給它添加一個值: NSExceptionDomains,類型為字典類型;
把需要的支持的域添加給NSExceptionDomains
其中域作為Key,類型為字典類型。
每個域下面需要設置3個屬性:
NSIncludesSubdomains、
NSExceptionRequiresForwardSecrecy、NSExceptionAllowsInsecureHTTPLoads。

均為Boolean類型,值分別為YES、NO、YES

QQ需要設置的域為:

QQ域

或者以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方式訪問的域名進行設置, 比較麻煩, 建議使用第一種方式.

到此, 集成及適配結束...

PS: 在使用相關API的時候, 需要新建橋接頭文件, 或者在已有橋接頭文件內引用其頭文件:

#import <TencentOpenAPI/TencentOAuth.h>
#import <TencentOpenAPI/QQApiInterfaceObject.h>
#import <TencentOpenAPI/QQApiInterface.h>

PS: 新的SDK在接入后會報一串這個錯誤:

Redefinition of 'TencentAuthorizeState'
...

只需要修改頭文件中的這個文件內容: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ā)起授權申請:

var tencentAuth: TencentOAuth! 
        
self. tencentAuth = TencentOAuth(appId: qqAppID, andDelegate: self)

在方法 func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool 中添加回調:

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 的回調
            return  TencentOAuth.handleOpen(url)
        }
        
        return true
}

然后, 實現(xiàn)其代理方法:

func tencentDidLogin() {
        // 登錄成功后要調用一下這個方法, 才能獲取到個人信息
        self.tencentAuth.getUserInfo()
    }
    
    func tencentDidNotNetWork() {
        // 網(wǎng)絡異常
    }
    
    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 {
           // 獲取授權信息異常
        }
    }

最后在需要發(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登錄授權流程就完成了,并成功的獲取到了QQ用戶的相關信息, 即登錄成功.
上面我是在AppDelegate.swift方法內處理的回調, 我們可以完全寫在其他的類里面, 只需要將其代理對象設置為需要處理回調的對象即可.

三. 分享

一直在吐槽騰訊的開放平臺, 找個文檔真心不容易, 繞了一些彎路, 最后還是百度搜到了文檔地址, 確實很尷尬. 雖然找到了官方文檔說明, 但是不是特別詳細, 可以下載其詳細文檔來參考設置.

PS: 在配置工程的時候需要注意, 如果需要兼容舊版本的手機QQ, 需要額外添加一個 URL Scheme : QQ + 十六進制的AppID ,不足八位的在首部補0 ; 例如你的AppID為: 123456 , 其十六進制為: 1E240, 則, URL Scheme 為: QQ0001E240

兼容低版本QQ

不過, 現(xiàn)在的QQ版本都比較高了, 至于低于哪個版本需要兼容, 官方文檔沒有提, 如果你的開發(fā)中適配低版本QQ時不能成功分享, 不妨添加試試.

如果是單獨集成了分享, 在注冊APP的時候依然是使用下面這個方法:

TencentOAuth(appId: "appid", andDelegate: nil)

如果沒有登錄, 這里代理傳nil即可; 如果有登錄, 按登錄的設置即可;
在其代理類中實現(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分享返回的回調
                if rs.result == "0" {
                    // 分享成功
                    print("分享成功")
                } else {
                    print("分享失敗")
                }
            }
        }
    }
    
    func isOnlineResponse(_ response: [AnyHashable : Any]!) {
        
    }

雖然我們只用在代理方法 func onResp(_ resp: QQBaseResp!) 中處理分享的回調, 但其他兩個方法也是要實現(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 方法中添加回調:

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的標題"
        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的標題"
        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不需要設置, 設置了也看不到.
關于 shareDestType 參數(shù), 官方文檔指出必須設置, 否則無法正常工作, 我發(fā)現(xiàn)即使不設置, 默認也是吊起QQ發(fā)起分享的.

這算是一個比較大, 也是比較坑的一個大坑了, 花了很多時間, 做了一些無用功, 這也算是其API的一個不太友好的地方吧.

PS: 這里對 QQApiObject 的一個參數(shù) cflag 做一下說明, 這是設置要分享的內容到什么地方, 空間, 收藏, 聊天, 電腦等

// 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ù).

分享圖片
  • 單圖
func shareImageToQQ() {
        // 原圖 最大5M
        let img = UIImage(named: "1.jpg")
        let data = UIImageJPEGRepresentation(img!, 0.8)
        // 預覽圖 最大 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, 可以設置imgObj?.cflag = UInt64(kQQAPICtrlFlagQZoneShareOnStart), 也可以使用QQApiInterface.sendReq(toQZone: req)

  • 多圖
    多圖只能分享到 QQ收藏
func shareImagesToQQ() {
        // 多圖不支持分享到QQ, 如果設置, 默認分享第一張
        // 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!])
        // 設置分享目標為QQ收藏
        imgObj?.cflag = UInt64(kQQAPICtrlFlagQQShareFavorites)
        let req = SendMessageToQQReq(content: imgObj)
        
        QQApiInterface.send(req)
    }

PS: 這里在設置分享到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.itdecent.cn/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: "關注作者流火緋瞳", 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: "視頻預覽圖片URL地址")
        
        let obj = QQApiVideoObject(url: url!, title: "分享的視頻名稱", description: "視頻內容描述", 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登錄分享的所有內容, 如有不正確的地方, 還請評論指出, 或者私信;

文章涉及的demo在Github LQThirdParty, 歡迎Star | Fork

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容