Sobes.tech
Middle+

Come usare l'architettura MVC per evitare il sovraccarico del controller?

sobes.tech AI

Risposta dell'AI

MVC (Model-View-Controller) in iOS spesso porta a controller "spessi" a causa della collocazione della logica di business e di presentazione al suo interno. Per risolvere questo problema, si possono usare i seguenti approcci:

  • Esternalizzare la logica di presentazione nella View: Creare UIView personalizzati o celle di tabelle/collezioni che si occupano di mostrare i dati e gestire le azioni di base dell'utente (ad esempio, pressioni sui pulsanti all'interno della cella). Il controller configura solo la View con i dati necessari.

    class MyCustomView: UIView {
        private let label = UILabel()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            addSubview(label)
            // Configurazione constraints, ecc.
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func configure(with text: String) {
            label.text = text
        }
    
        // Gestione eventi UIControlEvents, ad esempio tramite addTarget
    }
    
  • Esternalizzare la logica di business in Modelli o oggetti Service separati: Creare classi responsabili di operazioni specifiche (ad esempio, lavoro con rete, database, validazione dei dati). Il controller delega l'esecuzione di questi compiti a tali oggetti.

    class UserService {
        func fetchUsers(completion: @escaping ([User]?, Error?) -> Void) {
            // Logica di richiesta di rete
        }
    }
    
    class ProfileViewController: UIViewController {
        private let userService = UserService()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            userService.fetchUsers { users, error in
                // Aggiornamento UI in base ai dati ricevuti o errore
            }
        }
    }
    
  • Utilizzo di Protocols Data Source e Delegate: Implementare i protocolli UITableViewDataSource, UICollectionViewDataSource, UITableViewDelegate, UICollectionViewDelegate in classi separate, non nel controller stesso. Il controller avrà solo istanze di queste classi.

    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
            // Caricamento dati e passaggio a dataSource.update(users:)
        }
    }
    
  • Applicazione del Pattern Coordinator: Creare oggetti separati responsabili della navigazione tra gli schermi. I controller sanno solo che devono passare al passo successivo, ma non quale controller sarà il successivo.

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

Questi approcci consentono di alleggerire il controller, rendendolo più "sottile" e concentrato sulla gestione del ciclo di vita della View e sulla coordinazione tra Model e View, piuttosto che sull'esecuzione di tutta la logica dell'applicazione.