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) } } }