iOS集成QQ(TencentOpenAPI),封装单例

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

原文地址:https://www.cnblogs.com/qingzZ/p/14062761.html