swift项目第十天:网络请求工具类的封装

import UIKit

/*
 必须先导入头文件:import AFNetworking
 */
import AFNetworking

//MARK:-0:定义枚举:以枚举定义请求网络的get和post
/*
   swift的枚举不仅包括了基本数据类型还包括了字符串
 */
enum RequestType : String {
    
   case GET = "GET"
   case POST = "POST"
    
}
/*总结一:
 1:新建网络请求类RHNetWorkTool继承于AFHTTPSessionManager
 2:将该类设置为单例:单例的创建方法:static let shareInstance: RHNetWorkTool = RHNetWorkTool() 或是闭包形式
 static let shareInstance: RHNetWorkTool = {//创建对象,设置对象属性,并返回该对象}(),static修饰的属性变量为类属性,let可以保证线程是安全的
 3:继承于AFHTTPSessionManager,设置可以接受的contentTypes:
  let netWorkTool = RHNetWorkTool()
  netWorkTool.responseSerializer.acceptableContentTypes?.insert("text/html")
 4:枚举的使用:swift的枚举不仅包括了基本数据类型还包括了字符串,外部调用枚举的时候要加上 .,.GET
 enum RequestType : String {
 
 case GET = "GET"
 case POST = "POST"
 
 }
 
 */
class RHNetWorkTool: AFHTTPSessionManager {
  
     //MARK:-1:创建单例类:static修饰的属性变量为类属性,let可以保证线程是安全的
    static let shareInstance: RHNetWorkTool = {
        
        let netWorkTool = RHNetWorkTool()
        netWorkTool.responseSerializer.acceptableContentTypes?.insert("text/html")
        netWorkTool.responseSerializer.acceptableContentTypes?.insert("text/plain")
        netWorkTool.responseSerializer.acceptableContentTypes?.insert("application/json")
        netWorkTool.responseSerializer.acceptableContentTypes?.insert("text/JavaScript")
        netWorkTool.responseSerializer.acceptableContentTypes?.insert("text/json")
        return netWorkTool
    }()

}

 //MARK:-2:网络请求方法

/*
 总结二:
 1:一般在类中都将封装的方法封装在extension中,也就是为当前的类写一个分类
 2:该网络类的封装思路:1:新建网络类继承AFHTTPSessionManager ,将该类对象定义为单例,或是不用单例,或是直接用类方法去调用(class修饰的方法 class func ,为类方法,static修饰的属性为类属性),本类网络请求定义为单例 2:定义一个最底层的方法,其他的网络请求接口都将网络请求的urlPath,必传的参数封装在网络请求类的内部,在调用封装的最底层的方法 2:第二种封装方法:oc新浪微博的封装:1:新建HttpBaseTool继承于NSObject,在HttpBaseTool类中封装AFN代码,再定义HttpTool继承于NSobject,在HttpTool内调用HttpBaseTool封装的方法,HttpTool此类负责将参数模型转为字典,将请求的json转为结果模型,每个接口在封装为一个工具类继承HttpTool,将请求路径,必要参数都封装到工具类内部,并传入参数模型,结果模型,回调block 2:参数封装:参数也封装为一个模型(新建类继承NSObject,提供属性),对于每次接口都必传的参数,可以新建父类,父类提供初始化方法,父类并实现,创建对象并给固定参数赋值,然后让子类去继承,子类在调用初始化方法的时候会调用父类的初始化方法,并拥有了父类的属性 3:数据模型model的封装:1:model的封装采用MJExtension框架字典转模型(各种情况的使用场景)2:类似于表情键盘数据model的三级封装:1:首先定义一个大model,大model中定义数组,大数组中存放centerModel模型,在centerModel模型中在定义一个数组存放emojiModel数据模型 2:像类似于UI界面中分组cell的封装,也是将每一个分区看成是一个大model,在定义一个大数组,将每一个cell在看成一个数据模型,存放到大数组中 3:model要采用MVVM来进行封装。4:view的封装:1:初始化instanceType,构造初始化方法快速得到对象,在初始化方法中先设置自身的属性,再懒加载控件,再懒加载方法中设置控件的属性,将控件添加到父视图中,在layoutSubview中给控件设置frame,定义数据模型属性,重写数据模型属性的set方法,给控件赋值,由model来控制UI的显示,监听回调,监听:可以自己设置成为自己的代理监听自己 1:addTarget 2:通知 3:代理 4:重写系统的方法来实现监听 回调:1:协议代理 2:block 3:通知 4:多播委托,当层级较浅的时候可以采用12方式完成回调 ,当层级较深的时候,可以采用3,4的方式完成回调 5:控制器的封装:MVVM+分层封装思想:1:分层封装思想:将控制器进行模块化处理,尽量让控制器更加的轻量级,每一个模块封装为一个大view,对应一个数据model来显示内容,再看每一个模块内部是否可以再次封装(新浪微博cell的封装:就是将cell分层封装+mvvm模块化处理,让cell更加的轻量级:bgview含有工具条,和微博的bg,微博的bg内又划分为转发微博bg和未转发微博的bg,在转发与被转发微博的内部又将配图封装为一个大view,将每一张配图又封装为一个小view继承自UIImageView,来显示每一张的配图。MVVM思想:对model又进一层的封装,每一个封装的view都对应一个viewModel来显示内容,view显示的内容由model来控制)2:控制器的代码:1:可以重写其生命周期的方法来设置控制器内容 2:viewDidLoad中设置自身的属性,懒加载模块UI控件,设置控件的frame,处理数据模型model,处理数据模型model与控件的关系 3:代码的封装:1:每一个功能都封装为一个方法调用 2:在封装的方法内部若是涉及大量重复的代码,则再次抽方法封装,相同的部分封装在内部,不同的部分作为参数传递 2:对于业务逻辑的封装:若是和系统的类有关系,则写分类将业务逻辑封装在方法的内部,写工具类,单例(宏定义单例)或是类方法(在类方法中,static修饰的变量可作为类的属性,要想外部去调用,则提供其get方法供外界去访问,在类方法中也可以实现懒加载),继承:将相同的业务逻辑封装在父类,让子类去继承,父类可提供属性,供子类去重写返回标识来区分不同的子类。其他技巧:1:当系统类不能满足我们需求的时候我们可以去自定义类继承自系统的类,1:可以为系统的类扩充属性(图文混排的attachment),方法,或是给系统类添加新的控件(再利用kvc去替换掉系统的类) 2:重写系统类的layoutsubview方法,重新布局系统类控件的内部布局,或是添加新的子控件 3:利用runtime拿到系统的私有属性(也可以通过遍历系统子控件数组拿到我们想要的控件),去设置例如文本框占位颜色的设置 2:面向协议的开发:在开发中也可以将代理等大量业务逻辑的代码封装起来(转场动画代理的封装),再利用面向协议开发的思想,也就定义代理方法,设有参数和返回值,哪里能拿到封装类所需要的参数就设置谁为代理,获得所需要的值

 */

 //MARK:-3:底层AFN请求的封装
/*
 总结三:  1:封装最底层请求AFN的方法需要传入参数requestType请求的类型,urlstring,param,成功失败的回调
         2:[String : Any]:swift中数组或是字典中的数据类型不确定的时候,就用Any来表示,从数组或是字典中取出数据的时候,即使最初定义是确定的数据类型,取出来的也是Any?的可选类型,所以取出之后需要校验:1:guard else 校验 2:可选绑定校验 if let 3:??来进行校验 4:!= nil 直接进行强制拆包
         3:可选类型调用某个方法的时候,可以不进行校验,如果不为nil,执行方法,为nil则不会去执行该方法。左边是可选类型,给其进行赋值的时候可以赋值为确定的类型或是赋值为另一个可选类型
         4:在定义方法的时候,第一个参数一般都设置为内部参数:也就是_ + 空格来设置为内部参数。
         5:闭包的定义:
         let successCallback : (_ tast:URLSessionDataTask, _ result:Any?)->() =  { (tast:URLSessionDataTask, result:Any?)->() in
 
              finish(result,nil)
                
            }
 1:闭包的类型:()->(),在写闭包类型的时候,闭包的参数的时候,闭包的两个参数,都要设置内部参数,也就是设置为下划线+空格的形式,其中 let successCallback : (_ tast:URLSessionDataTask, _ result:Any?)->(),代表定义一个闭包。闭包的写法:{ (tast:URLSessionDataTask, result:Any?)->() in
              finish(result,nil)
            }
     以大括号写闭包的时候,其参数就不必设为内部参数了,其中若是没有返回值,则后面的->()可以省略
 { (tast:URLSessionDataTask, result:Any?) in
      finish(result,nil)
  }
 
 2:@escaping:为逃逸闭包,定义闭包的时候默认为非逃逸闭包,逃逸闭包的意思就是没有直接在定义闭包的方法中调用外界传进来的闭包,而是在方法外部或是其他方法内调用了闭包,这样的闭包称谓逃逸闭包,这样的闭包必须用关键字@escaping来修饰,写在闭包类型之前
 3:闭包的循环引用:当闭包产生循序引用的时候应[weak self]来消除循环引用,写在闭包类型的前面,在闭包中调用self的时候就用self?,在闭包中调用当前类的属性的时候,必须加self,否则会报错
 
 6:定义完最底层的数据请求方法后,1:在回调的闭包中定义的result和error都定义为可选类型,因为有可能有值也有可能没有值,分别定义为Any? 和 NSError?的可选类型。 AnyObject是一个协议,Any是零个协议!AnyObject用于任何类实例,而Any用于任何变量。 2:在定义失败和成功的回调闭包,回调数据,根据requestType的请求类型的不同,调用AFN不同的方法,将回调闭包分别传入
 7:as的使用:1:as 单独的as用于将swift字符串转为oc字符串 2:转为可选类型:as? 将一个类型转为可选类型就用as?,也可以error as NSError?,error为可选类型 3:若是强制转化类型就用:as!,其中强转的话必须是将可选类型进行强转
 
 8:一般将项目中用到的常量,通知名,url路径等封装在一个文件中作为全局变量,新建-swiftfile,并导入import UIKit框架
 
 */
extension RHNetWorkTool  {
    
    /*
     AnyObject是一个协议,Any是零个协议!AnyObject用于任何类实例,而Any用于任何变量。
     */
    func request(_ requestType:RequestType,urlString:String,param:[String : Any],finish:@escaping (_ result:Any?, _ error:NSError?)->())  {
        
        //1:定义成功和失败的回调闭包
        let successCallback : (_ tast:URLSessionDataTask, _ result:Any?)->() =  { (tast:URLSessionDataTask, result:Any?)->() in
            
            finish(result,nil)
        }
        
        let failureCallback = { (task:URLSessionDataTask?, error:Error?) in
            
            finish(nil,error as NSError?)
        }
        
        //2:发起数据请求
        if requestType == .GET {
            
            get(urlString, parameters: param, progress: nil, success:successCallback, failure:failureCallback)
            
        }else{
            
            post(urlString, parameters: param, progress: nil, success: successCallback, failure: failureCallback)
        }
        
    }
    
}
 //MARK:-3:请求accesstoken的接口
extension RHNetWorkTool {
    
    func requestWithAccessToken(_ code:String,finished:@escaping (_ result:Any?,_ error:NSError?)->()) {
        
        let paramDic = ["client_id":APPKEY,"client_secret":AppSecret,"grant_type":"authorization_code","code":code,"redirect_uri":RedirectURI]
        
        
        request(.POST, urlString: accesstokenPath, param: paramDic) { (result:Any?, error:NSError?) in
            
            finished(result,error)
        }
    }
    
}
原文地址:https://www.cnblogs.com/cqb-learner/p/5992722.html