Sobes.tech
Middle+

Comment utiliser l'architecture MVC pour éviter la surcharge du contrôleur?

sobes.tech IA

Réponse de l'IA

MVC (Modèle-Vue-Contrôleur) dans iOS conduit souvent à des contrôleurs "lourds" en raison de la placement de la logique métier et de la logique de présentation en eux. Pour résoudre ce problème, on peut utiliser les approches suivantes:

  • Externaliser la logique de présentation dans la Vue: Créer des UIView personnalisés ou des cellules de tableau/collection qui sont responsables de l'affichage des données et de la gestion des actions utilisateur de base (par exemple, appuis sur des boutons dans la cellule). Le contrôleur ne fait que configurer la Vue avec les données nécessaires.

    class MyCustomView: UIView {
        private let label = UILabel()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            addSubview(label)
            // Configuration des contraintes, etc.
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func configure(with text: String) {
            label.text = text
        }
    
        // Gestion des événements UIControlEvents, par exemple via addTarget
    }
    
  • Externaliser la logique métier dans des Modèles ou des objets Service séparés: Créer des classes responsables d'opérations spécifiques (par exemple, travail avec le réseau, la base de données, la validation des données). Le contrôleur délègue l'exécution de ces tâches à ces objets.

    class UserService {
        func fetchUsers(completion: @escaping ([User]?, Error?) -> Void) {
            // Logique de requête réseau
        }
    }
    
    class ProfileViewController: UIViewController {
        private let userService = UserService()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            userService.fetchUsers { users, error in
                // Mise à jour de l'UI basée sur les données reçues ou erreur
            }
        }
    }
    
  • Utilisation de Protocoles Data Source et Delegate: Implémentation des protocoles UITableViewDataSource, UICollectionViewDataSource, UITableViewDelegate, UICollectionViewDelegate dans des classes séparées, et non dans le contrôleur lui-même. Le contrôleur ne possède que des instances de ces classes.

    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
            // Chargement des données et passage à dataSource.update(users:)
        }
    }
    
  • Application du Pattern Coordinator: Création d'objets séparés responsables de la navigation entre les écrans. Les contrôleurs savent seulement qu'ils doivent passer à l'étape suivante, mais pas quel contrôleur sera le suivant.

    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()
            // Configuration du délégué, dataSource, etc.
            navigationController.pushViewController(usersVC, animated: false)
        }
    
        func showUserDetails(user: User) {
            let userDetailsVC = UserDetailsViewController(user: user)
            navigationController.pushViewController(userDetailsVC, animated: true)
        }
    }
    

Ces approches permettent de décharger le contrôleur, le rendant plus "mince" et concentré sur la gestion du cycle de vie de la Vue et la coordination entre Model et Vue, plutôt que d'exécuter toute la logique de l'application.