Sobes.tech
Middle+

Kako koristiti MVC arhitekturu da izbegnemo preopterećenje kontrolera?

sobes.tech АИ

Одговор од АИ

MVC (Model-View-Controller) u iOS često dovodi do "debelih" kontrolera zbog smeštanja poslovne logike i logike prikaza u njih. Za rešavanje ovog problema, mogu se koristiti sledeći pristupi:

  • Premeštanje logike prikaza u View: Kreiranje prilagođenih UIView ili ćelija tabele/kolekcije koje su odgovorne za prikaz podataka i obradu osnovnih akcija korisnika (npr. pritisci na dugmad unutar ćelije). Kontroler samo konfiguriše View potrebnim podacima.

    class MyCustomView: UIView {
        private let label = UILabel()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            addSubview(label)
            // Podešavanje constraints itd.
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func configure(with text: String) {
            label.text = text
        }
    
        // Obrada događaja UIControlEvents, na primer putem addTarget
    }
    
  • Premeštanje poslovne logike u Model ili posebne Service objekte: Kreiranje klasa koje su odgovorne za specifične operacije (npr. rad sa mrežom, bazom podataka, validacijom podataka). Kontroler delegira izvršenje ovih zadataka tim objektima.

    class UserService {
        func fetchUsers(completion: @escaping ([User]?, Error?) -> Void) {
            // Logika mrežnog zahteva
        }
    }
    
    class ProfileViewController: UIViewController {
        private let userService = UserService()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            userService.fetchUsers { users, error in
                // Ažuriranje UI na osnovu dobijenih podataka ili greške
            }
        }
    }
    
  • Korišćenje protokola Data Source i Delegate: Implementacija UITableViewDataSource, UICollectionViewDataSource, UITableViewDelegate, UICollectionViewDelegate u posebnim klasama, a ne u samom kontroleru. Kontroler će samo posedovati instance ovih klasa.

    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
            // Učitavanje podataka i prosleđivanje u dataSource.update(users:)
        }
    }
    
  • Primena Pattern-a Koordinator: Kreiranje posebnih objekata koji su odgovorni za navigaciju između ekrana. Kontroleri znaju samo da treba da pređu na sledeći korak, ali ne i koji će kontroler biti sledeći.

    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()
            // Podešavanje delegate, dataSource itd.
            navigationController.pushViewController(usersVC, animated: false)
        }
    
        func showUserDetails(user: User) {
            let userDetailsVC = UserDetailsViewController(user: user)
            navigationController.pushViewController(userDetailsVC, animated: true)
        }
    }
    

Ovi pristupi pomažu da se rastereti kontroler, čineći ga "tanijim" i fokusirajući se na upravljanje životnim ciklusom View i koordinaciju interakcije između Model i View, a ne na izvršavanje cele logike aplikacije.