iOS Design Patterns Part3: MVC-N

   demo传送门

iOS Design Patterns Part3: MVC-N_第1张图片

   欢迎回来,这一章节你会了解到MVC-N设计模式。让我们快速回顾一下MVC设计模式,MVC把文件按照models、views 、controllers.分类

   但事实不会这么简单,比如:谁来处理点击事件、谁来处理数据加载、谁来负责把数据展示在View上?

   啊哈!这三种类型实际上是重叠的。那么,重叠代码实际上在哪里呢?处理点击事件看起来是controller的任务,所以在controller处理它;数据加载谁来负责呢,好像还是控制器,把model的数据给View赋值呢?好吧,这些工作controller好像都能做,对么?

iOS Design Patterns Part3: MVC-N_第2张图片

   在你意识到控制器已经很大的时候,控制器已经有几千行代码了,这就是所谓的 massive view controller problem

iOS Design Patterns Part3: MVC-N_第3张图片
massive view controller problem

   幸好,我们还有办法解决它,当当当当!~~
MVC-N闪亮登场!

   MVC-N看起来比MVC更好,毕竟他按类型分成了四部分,四比三多,所以四个比三个更好,对吧,哈哈。

iOS Design Patterns Part3: MVC-N_第4张图片

   相对于现在demo中使用的复制网络请求代码(这当然是不太好的),MVC—N创建了一个 network client来处理网络请求

   Network clients要做的事情用开发术语来说就是:发起网络请求,把相应数据的json数据处理成model模型,然后通过闭包(block)回调传回view controller

   在接下来你要对demo开动了,要完成上述内容:创建一个network client,然后把现有的网络请求逻辑放到network client里,最后重构现有的controller来使用我们的network client

   打开demoStart,模拟器运行,点击Get Started,然后你会看到一个Cleaning Services table view controller

   这里有两个选项Home ServicesBusiness Services,我们选第一个(Home Services)。

   点击后会presents出来 Home Products View Controller,这个controller会发起一个网络请求来获取Home products信息。

   如果网络很快你看不到加载过程的话,不要担心,下拉一下你就会在状态栏看到那个可爱的小菊花了(这当然表示正在进行网络通讯)

   我们返回然后选择Business Services同样会presentsBusiness Products View Controller并通过网络加载Business products数据

   让我们打开代码来看看项目现在的情况吧,选择Cleaning Services—> Controllers 组.可以看到Business Products View ControllerHome Products View Controller

   这两个类都有load products方法且内容基本一致,唯一的不同就是请求URL的末端不同:Home Products View Controller末端是home Business Products View Controller末端是Business

iOS Design Patterns Part3: MVC-N_第5张图片

   错误处理逻辑和json解析逻辑也近乎一摸一样,这样看来重复代码太多太多了。所以我们要使用Model-View-Controller Networking设计模式,并创建一个network client来处理网络请求,使用network client来消除这两个控制器的重复代码。

   首先我们要创建一个新的组叫networking,在这之下还要创建两个组ExtensionsModels

   然后打开你下载的课件的根目录--->Resources目录,你会发现几个相关的网络文件,但他们现在还不适用于MVC-N设计模式,需要我们一会稍作修改。现在直接把这些文件拖进project就好了。

   首先把Int+HTTPStatusCodeUIImageView+URL拖进Extensions组中不要忘记勾选Copy Items if needed

   然后把NetworkError放到Models中,最后把NetworkClient直接拖进Networking组,搞定!

iOS Design Patterns Part3: MVC-N_第6张图片

   让我们快速把这几个文件过一眼,Int+HTTPStatusCode提供了一个简单的判断HTTP状态码是否在200-300之间。(即服务器返回请求成功状态)

   UIImageView+URL提供了一个通过URL快速设置imgView图片的方法(类似于大家常用的SDImage)如果你看不懂这些代码,don’t panic,这一点也不耽误你学习MVC-N

注释:discardableResult :在Swift3中,如果没有使用方法的返回值,会报出警告,使用@discardableResult关键字取消警告

   Network Error是一个枚举,作用是把 HTTP Status Code error处理成一个简单的模型,方便我们后续使用。

extension NetworkError: Equatable {
  public static func ==(lhs: NetworkError, rhs: NetworkError) -> Bool {
    return lhs.statusCode == rhs.statusCode
  }
}

注释:基本的枚举类型无需实现==就可以就行比较,但是对于非基本enum的 需要重写==运算符。

   当然我们最感兴趣的还是network client,因为我们要把现有的网络请求逻辑放在这里

   现在这个类只有一个初始化方法shared(),这里通过读取server environments plist获取root url。如果这个地址要经常改变的话,这样比直接写死要好一些,当然这只是个人喜好。实际上你怎么处理都无所谓。

   shared()是一个singletons方法,这里不去讨论程序员们对singletons的看法,或好或坏,至少singletons用在这里是没错的。

   下一步我们要把重复的网络请求移动到network client里来,首先我们创建一个新的方法

    public func getProducts(forType type: Product.ProductType,
                            success _success: @escaping ([Product]) -> Void,
                            failure _failure: @escaping (NetworkError) -> Void) {
        let success: ([Product]) -> Void = { products in
            DispatchQueue.main.async { _success(products) }
        }
        let failure: (NetworkError) -> Void = { error in
            DispatchQueue.main.async { _failure(error) }
        }
        let url = baseURL.appendingPathComponent("products/\(type.rawValue)")
       
        let task = session.dataTask(with: url, completionHandler: { (data, response, error) in
           
            guard let httpResponse = response as? HTTPURLResponse,
                httpResponse.statusCode.isSuccessHTTPCode,
                let data = data,
                let jsonObject = try? JSONSerialization.jsonObject(with: data),
                let json = jsonObject as? [[String: Any]] else {
                    if let error = error {
                        failure(NetworkError(error: error))
                    } else {
                        failure(NetworkError(response: response))
                    }
                    return
            }
           
            let products = Product.array(jsonArray: json)
            success(products)
        })
       
        task.resume()
    }

   需要传入一个product的枚举类型,用来表明我们需要请求哪种数据,是Business还是Home。然后把成功或失败的结果通过闭包(block)在主线程返回。

   network client不会关心请求的结果是什么,只是单纯的把数据通过成功或失败的闭包传递出去

   让我们快速的过一下这个方法,事实上,我们首先要确保HTTPURLResponse存在,然后验证httpResponse返回了成功的状态码,最后要确保返回的json数组格式正确,如果有任何一项不成立,我们认为网络请求失败,返回请求错误。

   最后task.resume()启动任务

   OK,到现在我们最后需要做的就是去Business Products View ControllerHome Products View Controller使用我们新写好的这个方法了。

   打开 Business Products View Controller,添加一个network client作为属性。然后更新 load products()方法

添加一个network client作为属性
iOS Design Patterns Part3: MVC-N_第7张图片
更新后的load products()方法

   同理,接下来修改Home Products View Controller,唯一的不同是传入的typebusiness变为product

   最后重新运行程序,没有出现任何问题,完美!

   还有一些其他小问题留在challenge(在课件文件里)里面你自己去实现吧!
   从controller中移除网络请求是解决massive view controller问题一个好的开始,但这样还不能完全解决这个问题,

   关注我们接下来的教程,你会学到其他设计模式来继续解决这个问题。

   拜拜。

   demo传送门

你可能感兴趣的:(iOS Design Patterns Part3: MVC-N)