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), options = (options)") TGSLOG(message: "url.host = (url.host ?? ""), 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