1.下載sdk
2.按照文檔拖到工程里面
3.info.list 添加LSApplicationQueriesSchemes ,右側array
<key>LSApplicationQueriesSchemes</key>
<array>
<string>tim</string>
<string>mqq</string>
<string>mqqapi</string>
<string>mqqbrowser</string>
<string>mttbrowser</string>
<string>mqqOpensdkSSoLogin</string>
<string>mqqopensdkapiV2</string>
<string>mqqopensdkapiV4</string>
<string>mqzone</string>
<string>mqzoneopensdk</string>
<string>mqzoneopensdkapi</string>
<string>mqzoneopensdkapi19</string>
<string>mqzoneopensdkapiV2</string>
<string>mqqapiwallet</string>
<string>mqqopensdkfriend</string>
<string>mqqopensdkavatar</string>
<string>mqqopensdkminiapp</string>
<string>mqqopensdkdataline</string>
<string>mqqgamebindinggroup</string>
<string>mqqopensdkgrouptribeshare</string>
<string>tencentapi.qq.reqContent</string>
<string>tencentapi.qzone.reqContent</string>
<string>mqqthirdappgroup</string>
<string>mqqopensdklaunchminiapp</string>
<string>mqqopensdkproxylogin</string>
<string>mqqopensdknopasteboard</string>
</array>
封裝文件
import UIKit /// debug模式輸出日志log func TGSLOG<Message>(message: Message, fileName: String = #file, methodName: String = #function, lineNumber: Int = #line){ #if DEBUG print("\((fileName as NSString).pathComponents.last ?? "").\(methodName)[\(lineNumber)]:\(message)") #endif } ///QQ授權成功的回調信息 struct TGSQQOAuthInfoStruct { ///Access Token憑證,用於后續訪問各開放接口 var accessToken = "" ///用戶授權登錄后對該用戶的唯一標識 var openId = "" ///Access Token的失效期 var expirationDate:Date = Date() } /* ["province": 山東, "level": 0, "is_yellow_year_vip": 0, "figureurl_qq": http://thirdqq.qlogo.cn/g?b=oidb&k=0hkIrnJWFA4Nbpt4KiaSiaNA&s=640&t=1557536217, //圖片尺寸:640x640 "figureurl_type": 1, "is_yellow_vip": 0, "figureurl_qq_2": http://thirdqq.qlogo.cn/g?b=oidb&k=0hkIrnJWFA4Nbpt4KiaSiaNA&s=100&t=1557536217,//圖片尺寸:100x100 "figureurl_1": http://qzapp.qlogo.cn/qzapp/101788689/9285D568E36F2CC5AF78085C38BF28AA/50,//圖片尺寸:50x50 "year": 1990, "vip": 0, "msg": , "gender_type": 1, "city": 菏澤, "gender": 男, "figureurl_2": http://qzapp.qlogo.cn/qzapp/101788689/9285D568E36F2CC5AF78085C38BF28AA/100,//圖片尺寸:100x100 "yellow_vip_level": 0, "constellation": , "figureurl": http://qzapp.qlogo.cn/qzapp/101788689/9285D568E36F2CC5AF78085C38BF28AA/30,//圖片尺寸:30x30 "nickname": 懂事長qingzZ, "ret": 0, "is_lost": 0, "figureurl_qq_1": http://thirdqq.qlogo.cn/g?b=oidb&k=0hkIrnJWFA4Nbpt4KiaSiaNA&s=40&t=1557536217]//圖片尺寸:40x40 */ ///QQ授權成功的用戶信息 struct TGSQQUserInfoStruct { ///出生年 var year = "" ///省 var province = "" ///城市 var city = "" ///昵稱 var nickname:String = "" ///性別 男/女 var gender:String = "" ///圖片原圖鏈接 var imageUrl:String = "" init(jsonData:JSON) { year = jsonData["year"].stringValue province = jsonData["province"].stringValue city = jsonData["city"].stringValue nickname = jsonData["nickname"].stringValue gender = jsonData["gender"].stringValue imageUrl = jsonData["figureurl_qq"].stringValue } } /** * QQ操縱單例 */ class TGSQQManager: NSObject { /// 創建單例對象 static let share = TGSQQManager() /// 重載 init() 方法,使其對外不可見,不可以在外部調用,防止在外部創建實例 private override init() {} /// 重載 copy方法 /// - Returns: self override func copy() -> Any { return self } /// 重載 mutableCopy方法 /// - Returns: self override func mutableCopy() -> Any { return self } ///授權對象 private var tencentOAuth:TencentOAuth! /// 發起授權成功 fileprivate var authInfoSuccess:((_ authInfo: TGSQQOAuthInfoStruct?) -> Void)? /// 發起授權獲取用戶信息成功 fileprivate var userInfoSuccess:(( _ userInfo:TGSQQUserInfoStruct?) -> ())? /// 發起授權失敗failure fileprivate var authFailure: ((_ error: String) -> Void)? /// 分享結果 fileprivate var shareResult: ((_ isSuccess: Bool, _ description: String) -> Void)? } //MARK: - 注冊方法 extension TGSQQManager{ /// 注冊 func registerQQ(){ self.tencentOAuth = TencentOAuth.init(appId: qqAppID, andUniversalLink: qqUniversalLink, andDelegate: self) } /// 判斷用戶是否安裝QQ func iphoneQQInstalled() -> Bool{ // return QQApiInterface.isQQInstalled() && QQApiInterface.isQQSupportApi() return TencentOAuth.iphoneQQInstalled() } /// 發起授權請求 /// - Parameters: /// - loginSuccess: 成功信息 /// - loginFailure: 失敗提醒 func startAuth(authInfoSuccess:((_ info: TGSQQOAuthInfoStruct?) -> Void)?, userInfoSuccess:(( _ userInfo:TGSQQUserInfoStruct?) -> ())?, authFailure: ((_ error: String) -> Void)?){ // 需要獲取的用戶信息 let permissionsArr = [kOPEN_PERMISSION_GET_INFO, kOPEN_PERMISSION_GET_USER_INFO,kOPEN_PERMISSION_GET_SIMPLE_USER_INFO] self.tencentOAuth.authorize(permissionsArr, inSafari: false) self.authInfoSuccess = authInfoSuccess self.userInfoSuccess = userInfoSuccess self.authFailure = authFailure } /// appdelegate 使用 打開qq /// - Parameter url: qq啟動第三方應用時傳遞過來的URL /// - Returns: 成功返回YES,失敗返回NO func handleOpen(open url: URL) -> Bool{ TencentOAuth.handleOpen(url) return QQApiInterface.handleOpen(url, delegate: self) } /// appdelegate 使用:打開通用鏈接 func handleOpenUniversalLink(open url: URL) -> Bool{ TencentOAuth.handleUniversalLink(url) return QQApiInterface.handleOpenUniversallink(url, delegate: self) } } //MARK: 代理 - QQApiInterfaceDelegate extension TGSQQManager:QQApiInterfaceDelegate{ ///QQApiInterfaceDelegate func onReq(_ req: QQBaseReq!) { } ///QQApiInterfaceDelegate func onResp(_ resp: QQBaseResp!) { if let qqResp = resp as? SendMessageToQQResp, let type = QQApiInterfaceRespType(rawValue: UInt(qqResp.type)){ switch type { case QQApiInterfaceRespType.ESENDMESSAGETOQQRESPTYPE: if let result = qqResp.result{ if result == "0" { self.shareResult?(true,"分享成功") }else if result == "-4"{ self.shareResult?(false,"取消分享") }else{ self.shareResult?(false,"分享失敗") } }else{ self.shareResult?(false,"分享失敗") } default:break } } } ///QQApiInterfaceDelegate func isOnlineResponse(_ response: [AnyHashable : Any]!) { } } //MARK: 代理 - TencentSessionDelegate extension TGSQQManager: TencentSessionDelegate{ ///登錄成功后的回調 func tencentDidLogin() { if tencentOAuth.getUserInfo(){ var tempOAuthInfoStruct = TGSQQOAuthInfoStruct() tempOAuthInfoStruct.openId = tencentOAuth.openId tempOAuthInfoStruct.expirationDate = tencentOAuth.expirationDate tempOAuthInfoStruct.accessToken = tencentOAuth.accessToken self.authInfoSuccess?(tempOAuthInfoStruct) }else{ TGSLOG(message: "沒有獲取到用戶個人信息") self.authInfoSuccess?(nil) } } /// 登錄失敗后的回調 /// - Parameter cancelled: 用戶取消 func tencentDidNotLogin(_ cancelled: Bool) { if cancelled{ TGSLOG(message: "用戶點擊取消按鍵,主動退出登錄") authFailure?("取消登錄") }else{ TGSLOG(message: "其他原因,導致登錄失敗") authFailure?("授權失敗") } } ///沒有網絡連接 func tencentDidNotNetWork() { authFailure?("無網絡,請檢查您的網絡設置") } /// 獲取用戶個人信息回調 func getUserInfoResponse(_ response: APIResponse!) { let queue = DispatchQueue(label: "aaLoginQueue") queue.async { if response.retCode == 0, let resDict = response.jsonResponse as? [String:Any]{ DispatchQueue.main.async {[weak self]in self?.userInfoSuccess?(TGSQQUserInfoStruct(jsonData: JSON(resDict))) } } else { DispatchQueue.main.async {[weak self] in self?.authFailure?("獲取授權信息異常") } } } } } //MARK: - 分享枚舉 enum TGSQQManagerShareEnum { // QQ列表 收藏 電腦 空間 禁止分享到空間 case QQ, Favorites, Dataline, QZone, QZoneForbid } //MARK: - 分享方法 extension TGSQQManager { /// 分享視頻 /// - Parameters: /// - url: URL /// - preImgUrl: 預覽圖 /// - title: 標題 /// - description: 描述信息 /// - shareType: 分享類型 /// - shareResult: 分享結果 func shareVideo(_ url: URL, preImgUrl: URL? = nil, title: String, description: String? = nil, shareType: TGSQQManagerShareEnum = .QQ, shareResult: ((_ isSuccess: Bool, _ description: String) -> Void)? = nil) { self.shareResult = shareResult let obj = QQApiVideoObject(url: url, title: title, description: description, previewImageURL: preImgUrl, targetContentType: QQApiURLTargetType.audio) switch shareType { case .QQ: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShare.rawValue) case .Favorites: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShareFavorites.rawValue) case .Dataline: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShareDataline.rawValue) case .QZone: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQZoneShareOnStart.rawValue) case .QZoneForbid: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQZoneShareForbid.rawValue) } let req = SendMessageToQQReq(content: obj) // 分享到QZone if shareType == .QZone { QQApiInterface.sendReq(toQZone: req) } else { // 分享到QQ QQApiInterface.send(req) } } /// 分享音樂 /// - Parameters: /// - url: URL /// - preImgUrl: 預覽圖 /// - title: 標題 /// - description: 描述信息 /// - shareType: 分享類型 /// - shareResult: 分享結果 func shareMusic(_ url: URL, title: String, description: String, preImgUrl: URL? = nil, shareType: TGSQQManagerShareEnum = .QQ, shareResult: ((_ isSuccess: Bool, _ description: String) -> Void)? = nil) { self.shareResult = shareResult let obj = QQApiAudioObject(url: url, title: title, description: description, previewImageURL: preImgUrl, targetContentType: QQApiURLTargetType.audio) switch shareType { case .QQ: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShare.rawValue) case .Favorites: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShareFavorites.rawValue) case .Dataline: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShareDataline.rawValue) case .QZone: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQZoneShareOnStart.rawValue) case .QZoneForbid: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQZoneShareForbid.rawValue) } let req = SendMessageToQQReq(content: obj) // 分享到QZone if shareType == .QZone { QQApiInterface.sendReq(toQZone: req) } else { // 分享到QQ QQApiInterface.send(req) } } /// 分享新聞 /// - Parameters: /// - url: URL /// - preImgUrl: 預覽圖 /// - title: 標題 /// - description: 描述信息 /// - shareType: 分享類型 /// - shareResult: 分享結果 func shareNews(_ url: URL, preUrl: URL? = nil, title: String? = nil, description: String? = nil, shareType: TGSQQManagerShareEnum = .QQ, shareResult: ((_ isSuccess: Bool, _ description: String) -> Void)? = nil) { self.shareResult = shareResult let obj = QQApiNewsObject(url: url, title: title, description: description, previewImageURL: preUrl, targetContentType: QQApiURLTargetType.news) switch shareType { case .QQ: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShare.rawValue) case .Favorites: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShareFavorites.rawValue) case .Dataline: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShareDataline.rawValue) case .QZone: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQZoneShareOnStart.rawValue) case .QZoneForbid: obj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQZoneShareForbid.rawValue) } let req = SendMessageToQQReq(content: obj) // 分享到QZone if shareType == .QZone { QQApiInterface.sendReq(toQZone: req) } else { // 分享到QQ QQApiInterface.send(req) } } /// 分享一組圖片 /// - Parameters: /// - images: 圖片數組 /// - preImage: 預覽圖 /// - title: 標題 /// - description: 描述信息 /// - shareResult: 分享結果 func shareImages(_ images: [Data], preImage: Data? = nil, title: String? = nil, description: String? = nil, shareResult: ((_ isSuccess: Bool, _ description: String) -> Void)? = nil) { self.shareResult = shareResult // 多圖不支持分享到QQ, 如果設置, 默認分享第一張 // k可以分享多圖到QQ收藏 guard images.count > 0 else { return } let imgObj = QQApiImageObject(data: images.first, previewImageData: preImage, title: title, description: description, imageDataArray: images) imgObj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShareFavorites.rawValue) let req = SendMessageToQQReq(content: imgObj) if Thread.current.isMainThread { QQApiInterface.send(req) } else { DispatchQueue.main.async { QQApiInterface.send(req) } } } /// 分享單張圖片 /// - Parameters: /// - imgData: 圖片數據 /// - thumbData: 預覽圖數據 /// - title: 標題 /// - description: 描述信息 /// - shareType: 分享類型 /// - shareResult: 分享結果 func shareImage(_ imgData: Data, thumbData: Data? = nil, title: String? = nil, description: String? = nil, shareType: TGSQQManagerShareEnum = .QQ, shareResult: ((_ isSuccess: Bool, _ description: String) -> Void)? = nil) { self.shareResult = shareResult // 原圖 最大5M // 預覽圖 最大 1M let imgObj = QQApiImageObject(data: imgData, previewImageData: thumbData, title: title, description: description) switch shareType { case .QQ: imgObj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShare.rawValue) case .Favorites: imgObj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShareFavorites.rawValue) case .Dataline: imgObj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShareDataline.rawValue) case .QZone: imgObj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQZoneShareOnStart.rawValue) case .QZoneForbid: imgObj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQZoneShareForbid.rawValue) } let req = SendMessageToQQReq(content: imgObj) if Thread.current.isMainThread { if shareType == .QZone { QQApiInterface.sendReq(toQZone: req) } else { QQApiInterface.send(req) } } else { DispatchQueue.main.async { if shareType == .QZone { QQApiInterface.sendReq(toQZone: req) } else { QQApiInterface.send(req) } } } } /// 分享文字 /// - Parameters: /// - text: 文字 /// - shareType: 分享類型 /// - shareResult: 分享結果 func shareText(_ text: String, shareType: TGSQQManagerShareEnum = .QQ, shareResult: ((_ isSuccess: Bool, _ description: String) -> Void)? = nil) { self.shareResult = shareResult let textObj = QQApiTextObject(text: text) textObj?.shareDestType = ShareDestType.QQ // 分享到QQ 還是TIM, 必須指定 switch shareType { case .QQ: textObj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShare.rawValue) case .Favorites: textObj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShareFavorites.rawValue) case .Dataline: textObj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQQShareDataline.rawValue) case .QZone: textObj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQZoneShareOnStart.rawValue) case .QZoneForbid: textObj?.cflag = UInt64(kQQAPICtrlFlag.qqapiCtrlFlagQZoneShareForbid.rawValue) } let req = SendMessageToQQReq(content: textObj) req?.message = textObj QQApiInterface.send(req) } }
使用:
AppDelegate.swift中 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { ///注冊微信 TGSWeChatManager.share.registeredWaChat() ///注冊QQ TGSQQManager.share.registerQQ() self.window = TGSWindowView(frame: UIScreen.main.bounds) self.window?.rootViewController = TGSAppRootTabController() self.window?.makeKeyAndVisible() return true } ///微信/QQ操作 extension AppDelegate{ ///iOS9 以上沒有配置通用鏈接 走這個方法 func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { //QQ回調 url.host:qzapp, url.scheme: "tencent" + qqAppID //微信回調 url.host:空 , url.scheme: wxAppID TGSLOG(message: "url = \(url), \n options = \(options)") TGSLOG(message: "url.host = \(url.host ?? ""), \n url.scheme = \(url.scheme ?? "")") return thirdHandleOpenUrl(open: url) } /// iOS9 以下沒有配置通用鏈接 走這個方法 func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { TGSLOG(message: "sourceApplication = \(sourceApplication ?? "")") return thirdHandleOpenUrl(open: url) } /// 沒有配置通用鏈接打開回調 private func thirdHandleOpenUrl(open url: URL) -> Bool{ if url.scheme == wxAppID{//微信的host 是空的 return TGSWeChatManager.share.handleOpenUrl(url: url) }else if url.scheme == "tencent" + qqAppID, url.host == "qzapp"{// "tencent" + qqAppID 因為qq的scheme必須這么寫 return TGSQQManager.share.handleOpen(open: url) } return TGSQQManager.share.handleOpen(open: url) } ///配置了通用鏈接 走這個方法 工程->targets->Signing&Capabilities->All-> + -> Associated Domains -> 鏈接名字 "applinks:818ps.com" func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { if userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL{ TGSLOG(message: "配置了通用鏈接 url = \(url)") if url.absoluteString.contains(wxAppID) { return TGSWeChatManager.share.handleOpenUniversalLink(activity: userActivity) }else if url.absoluteString.contains(qqAppID) {///待測試 return TGSQQManager.share.handleOpenUniversalLink(open: url) } return TGSQQManager.share.handleOpenUniversalLink(open: url) } return true; } }
參考:
[Swift]原生第三方接入: QQ篇--集成/登錄/分享
https://www.jianshu.com/p/c8db82d27b11
一步一步實現iOS QQ第三方登錄
https://www.jianshu.com/p/ab3c1b177841
