Adequate MVC for beginners and more

Small introduction


All 404! Most iOS developers do not understand how to work with Apple-MVC. Because of which there is unreasonable criticism, small projects try to write on architectures that are not intended for this, ViewControllers become huge, unreadable, and so on.

In this article I will show a simple way to make life easier for myself, life for myself, who has looked into the project after 3 months, and, of course, for all ViewControllers. Who is it suitable for? For beginners or developers with small projects that end up with a clean and beautiful structure.

Where does everyone drown?


To get started, let's look at everyone's favorite standard beautiful MVC.

image

Now let's look at what is being implemented by the majority.

image

Cool? No! Therefore, we will not do this, but will implement the first option.

Making the structure of beauty


We create the usual Single View App. I put Swift in the language selection field, not including anything else: neither tests, nor CoreDat's.

I will show by the example of creating a shop window, because we need to affect all parts of MVC, we will take the data from model to controller to distribute and show in view.

Okay, there is a task, there is an empty project. Now diving into the project, I will distribute everything into folders and create empty files so that you understand where they should lie in the future. Also attached a storyboard structure, so as not to waste time in the future. Do not forget to specify reuse id at cell, I wrote ProductCell.

image

Handsomely? Of course it's beautiful. We continue ...

Some magic swift


Now we can begin implementation. Let's start with a simple view. We need to create all IBOutlets, and the settings for our view.

//    ,   view     UIKit. 
import UIKit

class ShopView: UIView{
    //       storyboard. 
    @IBOutlet weak var tableView: UITableView!
    //      view. 
    func configure(){
        //     ,    ,      ...
        tableView.estimatedRowHeight = 50
        tableView.rowHeight = 100
    }
}

This is how our view will look like, in the configure () function, I just set the settings, but view needs to be used for animations, and for changing the title of the button’s title, etc. ... So primitively say? But why then use this advantage so little ?! So, for example, I use animations in the controller'e: view.hideBorders (self) and that's it, and view will already make a beautiful animation.

//    view  . 
func hideBorders(view: UIView){
    UIView.animate(withDuration: 0.3,
                   delay: 0,
                   options: .curveEaseInOut,
                   animations: {
           //         constraint. 
           self.createViewHeighConstraint.constant = view.bounds.height * 0.6
           //   ,   ,    . 
           self.editButton.layer.cornerRadius = 20
           self.postView.layer.shadowOpacity = 0
           self.postView.layer.cornerRadius = 0
           view.layoutIfNeeded()
    }, completion: nil)
}

Fire! But back to our project, time to implement model. And so we will have goods, and may their groups be.

//   ,   model       View.
// ,    ,       Foundation.
import Foundation
//      ,  . 
public struct Product {
  public let name: String
}
//    ,      ,   . 
public struct ProductGroup {
  public let products: [Product]
  public let title: String
}
//  swift   ()    extension. 
extension ProductGroup{
    //  ,     ( Product). 
    public static func vegetables() -> ProductGroup{
       //   .
        let products = [
            Product(name: ""),
            Product(name: ""),
            Product(name: "")
        ]
        //      "". 
        return ProductGroup(products: products, title: "")
    }
}

Great, our model is ready, it remains to assemble the controller, let's transgress. First of all, we will need to create two of our main entities, model and view. Then I will take out the controller settings in the private extension and transfer the data from model to our table.

//  controller'e      ,      UIKit.
import UIKit

class ShopController: UIViewController {
    //  -     .
    private var shopModel = ProductGroup.vegetables()
    
    //     (       )
    private var shopView: ShopView! {
        guard isViewLoaded else { return nil }
        return (view as! ShopView)
    }
    //      view,         controller'a  view  . 
    override func viewDidLoad() {
        super.viewDidLoad()
        //    view      .
        configure()
    }
}

private extension ShopController{
    //          .
    func configure(){
        shopView.tableView.delegate = self
        shopView.tableView.dataSource = self
    }
}

//      . 
extension ShopController: UITableViewDataSource, UITableViewDelegate{

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        //  -   -   
        return shopModel.products.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //   ,   reuse Id ,      .
        let cell = tableView.dequeueReusableCell(withIdentifier: "ProductCell")
        //    label  cell     . 
        cell?.textLabel?.text = shopModel.products[indexPath.row].name
        return cell!
    }
}

He promised to explain about view, didn’t forget?) Our shopView is a calculated property. Here we check isViewLoaded so as not to cause the view to load unintentionally when accessing this property. If the view is already loaded, we force it to be converted to ShopView.

That's it, our mvc is ready! It remains to press command + R and see three cells.

What do we have in the end?


  • Simple, clear architecture
  • Uniform load on model, view, controller
  • Easy reuse view
  • Easy extensibility of our project

Conclusion


I really hope that this article will help beginners to keep their projects clean, not to get lost in the code, but to concentrate on tasks. All good and less bugs ^^

All Articles