Sobes.tech
Middle+

Cum să folosești arhitectura MVC pentru a evita supraîncărcarea controlerului?

sobes.tech AI

Răspuns de la AI

MVC (Model-View-Controller) în iOS duce adesea la controlere "grosiere" din cauza plasării logicii de business și a logicii de prezentare în ele. Pentru a rezolva această problemă, se pot folosi următoarele abordări:

  • Deplasarea logicii de prezentare în View: Crearea de UIView personalizate sau celule de tabel/colecție care sunt responsabile pentru afișarea datelor și gestionarea acțiunilor de bază ale utilizatorului (de exemplu, apăsarea butoanelor în interiorul celulei). Controlerul configurează doar View-ul cu datele necesare.

    class MyCustomView: UIView {
        private let label = UILabel()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            addSubview(label)
            // Configurarea constrângerilor etc.
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func configure(with text: String) {
            label.text = text
        }
    
        // Gestionarea evenimentelor UIControlEvents, de exemplu prin addTarget
    }
    
  • Deplasarea logicii de business în Model sau în obiecte de Service separate: Crearea de clase responsabile pentru operațiuni specifice (de exemplu, lucrul cu rețeaua, baza de date, validarea datelor). Controlerul delegă aceste sarcini acestor obiecte.

    class UserService {
        func fetchUsers(completion: @escaping ([User]?, Error?) -> Void) {
            // Logica pentru cererea de rețea
        }
    }
    
    class ProfileViewController: UIViewController {
        private let userService = UserService()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            userService.fetchUsers { users, error in
                // Actualizarea UI pe baza datelor primite sau a erorii
            }
        }
    }
    
  • Utilizarea protocolului Data Source și Delegate: Implementarea UITableViewDataSource, UICollectionViewDataSource, UITableViewDelegate, UICollectionViewDelegate în clase separate, nu în controller. Controllerul va deține doar instanțe ale acestor clase.

    class UsersTableViewDataSource: NSObject, UITableViewDataSource {
        private var users: [User] = []
    
        func update(users: [User]) {
            self.users = users
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return users.count
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath)
            let user = users[indexPath.row]
            cell.textLabel?.text = user.name
            return cell
        }
    }
    
    class UsersViewController: UIViewController {
        @IBOutlet weak var tableView: UITableView!
        private let dataSource = UsersTableViewDataSource()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            tableView.dataSource = dataSource
            // Încărcarea datelor și transmiterea în dataSource.update(users:)
        }
    }
    
  • Aplicarea pattern-ului Coordinator: Crearea de obiecte separate responsabile pentru navigarea între ecrane. Controlerele știu doar că trebuie să treacă la pasul următor, dar nu știu exact care controller va fi următorul.

    protocol AppCoordinator: AnyObject {
        func start()
        func showUserDetails(user: User)
    }
    
    class MainAppCoordinator: AppCoordinator {
        private let navigationController: UINavigationController
    
        init(navigationController: UINavigationController) {
            self.navigationController = navigationController
        }
    
        func start() {
            let usersVC = UsersViewController()
            // Configurarea delegate-ului, dataSource etc.
            navigationController.pushViewController(usersVC, animated: false)
        }
    
        func showUserDetails(user: User) {
            let userDetailsVC = UserDetailsViewController(user: user)
            navigationController.pushViewController(userDetailsVC, animated: true)
        }
    }
    

Aceste abordări permit descărcarea controlerului, făcându-l mai "subțire" și concentrat pe gestionarea ciclului de viață al View-ului și pe coordonarea interacțiunii între Model și View, nu pe executarea întregii logici a aplicației.