美文网首页
iOS开发-OAuth授权(下篇)

iOS开发-OAuth授权(下篇)

作者: 少少少少少少少 | 来源:发表于2017-03-16 11:10 被阅读91次
    • 获取未授权RequestToken(登录界面)
      • 按照文档发送GET请求
    • 获取已经授权RequestToken(让用户登录)
      • 截取授权回调页code=后面字符串
    • 换取AccessToken(令牌)
      • 传递已经授权RequestToken, 和其它参数
    • 守护者和可选绑定
      • guard let value = xxx else {}
        • 有效避免{}嵌套, 提高阅读性
        • 条件表达式中的变量可以在后面使用
        • 条件表达式中的变量不能在{}中使用
      • if let value = xxx {}
        • 容易形成{}嵌套
        • 条件表达式中的变量不能在后面使用
        • 条件表达式中的变量可以在{}中使用
      • 守护者和可选绑定可以添加额外条件

    Swift
    // 两个条件同时为false进入{}
    guard let value = xxx where 条件表达式
    else
    {
    return true
    }

    // 两个条件同时为true进入{}
    if let value = xxx where 条件表达式
    {
    
    }
    ```
    
    • NSURLquery属性
      • 用于获取URL中的参数(?后面的内容)
    • 单例
      • dispatch
        • 原理默认dispatch_once_t等于0, 执行一次之后变为-1
          *Swift不推荐这样编写单例
    swift
        static var onceToken: dispatch_once_t = 0;
        static var instance: NetworkTools?;
        class func shareInstance() -> NetworkTools
        {
            print(onceToken)
            dispatch_once(&onceToken) { () -> Void in
                instance = NetworkTools()
            }
            return instance!
        }
        ```
    
    
    
        + 静态常量
            * let是线程安全的
            * let只能赋值一次
            * 推荐写法
    
        swift
        static let shareInstance:NetworkTools = NetworkTools()
        
    
        + 如何在创建单例时初始化对象
            * 静态常量 + 重写构造方法
            swift
            static let shareInstance:NetworkTools = NetworkTools()
            override init() {
                print("初始化操作")
            }
            
            * 静态常量 + 闭包
            swift
            static let shareInstance:NetworkTools = {
                let t = NetworkTools()
                print("初始化操作")
                // 对T进行初始化
                return t
            }()
            
    - AFN
        + Swift使用不同命名空间(不同项目)中的类需要导入头文件
        ```swift
        import UIKit
        import AFNetworking
    
        class NetworkTools: AFHTTPSessionManager {
    
            static let shareInstance: NetworkTools = {
                // 注意: 指定baseURL一定要以/结尾
               let url = NSURL(string: "https://api.weibo.com/")
               let tools =  NetworkTools(baseURL: url)
                tools.responseSerializer.acceptableContentTypes = NSSet(objects: "application/json", "text/json", "text/javascript", "text/plain") as? Set<String>
               return tools
            }()
        }
        ```
    
    - KVC
        + key必须和属性一一对应, 否则需要重写
        ```swift
        override func setValue(value: AnyObject?, forUndefinedKey key: String) {}
        ```
        + 在构造方法中使用KVC必须先调用super.init()
        + 基本数据类型属性必须初始化, 否则会报错
    
    - 打印对象
        + 重写CustomStringConvertible协议中的description属性
        ```swift
        override var description: String{
            let keys = ["access_token", "expires_in", "uid"]
            let dict = dictionaryWithValuesForKeys(keys)
            return "\(dict)"
        }
        ```
        + 注意Swift2.0之前该属性在Printable协议中
    
    - 归档解归档
        + 和OC一致
        ```swift
        // 写入文件时调用
        func encodeWithCoder(aCoder: NSCoder)
        {
            aCoder.encodeObject(access_token, forKey: "access_token")
            aCoder.encodeObject(expires_in, forKey: "expires_in")
            aCoder.encodeObject(uid, forKey: "uid")
        }
        // 从文件中读取出来时调用
        required init?(coder aDecoder: NSCoder)
        {
            access_token = aDecoder.decodeObjectForKey("access_token") as? String
            expires_in = aDecoder.decodeObjectForKey("expires_in") as! Int
            uid = aDecoder.decodeObjectForKey("uid") as? String
        }
        ```
        + 注意: 开发中谁最清楚怎么做, 方法就应该在谁里面
    
    - 路径
        - `Documents`
            - 需要保存由"应用程序本身"产生的文件或者数据,例如:游戏进度、涂鸦软件的绘图
            - 目录中的文件会被自动保存在 iCloud
            - 注意:不要保存从网络上下载的文件,否则会无法上架!
    
        - `Caches`
            - 保存临时文件,"后续需要使用",例如:缓存图片,离线数据(地图数据)
            - 系统不会清理 `cache` 目录中的文件
            - 就要求程序开发时,"必须提供 `cache` 目录的清理解决方案"
    
        - `Preference`s
            - 用户偏好,使用 `NSUserDefault` 直接读写!
            - 如果要想数据及时写入磁盘,还需要调用一个同步方法
    
        - `tmp`
            - 保存临时文件,"后续不需要使用"
            - `tmp` 目录中的文件,系统会自动清理
            - 重新启动手机,`tmp` 目录会被清空
            - 系统磁盘空间不足时,系统也会自动清理
    
    - 路径优化
        + 开发中经常需要往CachesDirectory/DocumentDirectory/TemporaryDirectory中存储数据
        + 为了方便拼接路径, 化繁为简应该对拼接实现进行封装
    
    

    Swift
    func docDir() -> String
    {
    // 1.拿到文件夹路径
    let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).last!
    // 2.拼接字符串
    // 注意: pathComponents方法, 会按照字符串的/来切割字符串, 如果给定的字符串中没有/那么就直接返回给定的字符串
    let temp = (self as NSString).pathComponents.last

        // 如果??前面有值, 那么??后面的代码不执行, 如果??前面没有值, 那么就执行??后面的代码
        let result = (path as NSString).stringByAppendingPathComponent(temp ?? "")
    
        // 3.返回结果
        return result
    }
    
    func cachesDir() -> String
    {
        // 1.拿到文件夹路径
        let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true).last!
         let temp = (self as NSString).pathComponents.last
        // 2.拼接字符串
        let result = (path as NSString).stringByAppendingPathComponent(temp ?? "")
    
        // 3.返回结果
        return result
    }
    
    func tmpDir() -> String
    {
        // 1.拿到文件夹路径
        let path = NSTemporaryDirectory()
    
         let temp = (self as NSString).pathComponents.last
        // 2.拼接字符串
        let result = (path as NSString).stringByAppendingPathComponent(temp ?? "")
    
        // 3.返回结果
        return result
    }
    
    
    - `??`
        + `??`前面不为`nil`,后面不执行
        + `??`前面为`nil`, 执行后面代码
    
    - 性能优化
        + 多次从文件中加载授权模型返回的结果一致
        + 从文件中加载模型非常消耗性能
        + 解决方案: 利用静态变量保存, 只加载一次
    
    - 自定义UICollection布局
        + 在显示cell之前会调用prepareLayout准备布局
        + 在prepareLayout方法中修改布局
    ```swift
    class NewfeatureLayout: UICollectionViewFlowLayout {
        // 该方法会在显示cell之前调用
        override func prepareLayout() {
            // 修改layout属性
            itemSize = UIScreen.mainScreen().bounds.size
            scrollDirection = UICollectionViewScrollDirection.Horizontal
            minimumInteritemSpacing = 0
            minimumLineSpacing = 0
    
            // 设置collectionView
            collectionView?.showsHorizontalScrollIndicator = false
            collectionView?.showsVerticalScrollIndicator = false
            collectionView?.bounces = false
            collectionView?.pagingEnabled = true
    
        }
    }
    
    • 监听cell是否完全显示
    override func collectionView(collectionView: UICollectionView, didEndDisplayingCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath){
    }
    
    • 获取当前显示cell的索引
    swift let path = collectionView.indexPathsForVisibleItems()
    
    • Damping动画
    /*
            第一个参数: 动画时长
            第二个参数: 延迟时长
            第三个参数: 震动幅度 0.0~1.0之间, 值越小震动越厉害
            第四个参数: 动画初始速度, 值越大开始就越快
            第五个参数: 附加选项
            第六个参数: 需要执行动画代码
            第七个参数: 动画执行完毕之后的回调
            */
            UIView.animateWithDuration(2.0, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 10, options: UIViewAnimationOptions(rawValue: 0), animations: { () -> Void in
                    self.startButton.transform = CGAffineTransformIdentity
                }, completion: { (_) -> Void in
                    XMGLog("动画执行完毕")
                    self.startButton.userInteractionEnabled = true
            })
    
    • 获取用户信息
      • 保证方法单一性, 不要做过多复杂操作
      • 将主动权交给调用者
    func loadUserInfo(finished: (dict: [String: AnyObject]?, error: NSError?)->())
        {
    
            // 断言: 用于程序员之间沟通
            // 断定access_token一定有值, 如果没有程序就会崩溃, 并且打印后面的message
            assert(access_token != nil, "必须有access_token才能使用该方法")
            assert(uid != nil, "必须有uid才能使用该方法")
    
            // 1.准备路径
            let path = "2/users/show.json"
            // 2.准备参数
            let parameters = ["access_token": access_token!, "uid": uid!]
            // 3.发送Get请求
            NetworkTools.shareInstance.GET(path, parameters: parameters, success: { (task, objc) -> Void in
    
                // 3.1.对服务器返回的数据进行安全校验
                guard let dict = objc as? [String: AnyObject] else
                {
                    XMGLog("服务器没有返回数据")
                    return
                }
    
                // 3.2从获取到的用户信息中取出昵称和头像
                self.avatar_large = dict["avatar_large"] as? String
                self.screen_name = dict["screen_name"] as? String
    
                // 3.3保存授权信息
    //            self.saveUserAccount()
                finished(dict: dict, error: nil)
    
                }) { (task, error) -> Void in
                    XMGLog(error)
                    finished(dict: nil, error: error)
            }
        }
    
    + 用到闭包, 不管三七二十一先写上 `()->() ` 再做其它修改
    
    • 界面切换流程

    • 检查新版本

      • 思路
        • 获取当前
        • 获取以前
        • 进行比较
        • 更新以前
        • 返回结果
    Swift
    /// 判断是否有新版本
        private func isNewVersion() -> Bool
        {
            // 1.获取软件当前的版本号
            guard let currentVersion = NSBundle.mainBundle().infoDictionary!["CFBundleShortVersionString"] as? String else
            {
                return false
            }
    
            // 2.获取软件以前的版本号
            let defaults = NSUserDefaults.standardUserDefaults()
            let sandboxVersion = (defaults.objectForKey("xmg") as? String) ?? "0"
    
            // 3.利用软件当前的版本号和以前的版本号进行比较
            // 如果当前 > 以前  --> 新版本
            //  1.0   0.0
            if currentVersion.compare(sandboxVersion) == NSComparisonResult.OrderedDescending
            {
                NSLog("有新版本")
                // 4.如果有新版本就用当前的软件版本号更新以前的软件版本号
                defaults.setObject(currentVersion, forKey: "xmg")
    //            defaults.synchronize() // iOS7以后不用写
                return true
            }
    
            NSLog("没有新版本")
            // 没有新版本
            return false
        }
    
    • 完善界面切换逻辑
      • 注意统一管理, 便于后期维护
      • 注意安全校验, 一般网络和通知都需要校验
    // 发送通知, 切换到首页
     NSNotificationCenter.defaultCenter().postNotificationName(SYPChangeRootViewControllerNotification, object: self, userInfo: ["message": true])
    
     // 发送通知切换到欢迎界面
      NSNotificationCenter.defaultCenter().postNotificationName(SYPChangeRootViewControllerNotification, object: self, userInfo: ["message": false])
    
      // 监听通知切换界面
      @objc private func switchRootViewController(notice: NSNotification)
        {
    //        NSLog(notice)
            // 1.取出userInfo中的message
            guard let info = notice.userInfo else
            {
                return
            }
            guard let flag = info["message"] as? Bool else
            {
                return
            }
    
            // 2.根据message对应的值, 决定对应的界面
            var sb = UIStoryboard(name: "Welcome", bundle: nil)
            if flag
            {
                // 跳转到首页
                sb = UIStoryboard(name: "Main", bundle: nil)
            }
            window?.rootViewController = sb.instantiateInitialViewController()!
        }
    

    相关文章

      网友评论

          本文标题:iOS开发-OAuth授权(下篇)

          本文链接:https://www.haomeiwen.com/subject/xqnkkttx.html