• 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

  • 相关阅读:
    单元测试之NUnit
    “Oracle.DataAccess.Client.OracleConnection”的类型初始值设定项引发异常
    功能强大的树状结构TreeGrid
    右键效果图
    可视化定义工作流(正在努力做......w)
    关于自动发送邮件换行等问题解决
    清空Cache
    .net发生类型为 System.OutOfMemoryException 的异常解决办法
    再谈xml
    Delphi日期函数大全
  • 原文地址:https://www.cnblogs.com/qingzZ/p/14062761.html
Copyright © 2020-2023  润新知