Sobes.tech
Middle+

¿Cómo usar la arquitectura MVC para evitar la sobrecarga del controlador?

sobes.tech AI

Respuesta de la IA

MVC (Modelo-Vista-Controlador) en iOS a menudo conduce a controladores "gruesos" debido a la colocación de lógica de negocio y lógica de presentación en ellos. Para resolver este problema, se pueden usar los siguientes enfoques:

  • Externalizar la lógica de presentación en la Vista: Crear UIView personalizados o celdas de tablas/colecciones que sean responsables de mostrar datos y manejar acciones básicas del usuario (por ejemplo, pulsaciones de botones dentro de la celda). El controlador solo configura la Vista con los datos necesarios.

    class MyCustomView: UIView {
        private let label = UILabel()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            addSubview(label)
            // Configuración de constraints, etc.
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func configure(with text: String) {
            label.text = text
        }
    
        // Manejo de eventos UIControlEvents, por ejemplo, mediante addTarget
    }
    
  • Externalizar la lógica de negocio en Modelos o en objetos de Servicio separados: Crear clases responsables de operaciones específicas (por ejemplo, trabajo con red, base de datos, validación de datos). El controlador delega la ejecución de estas tareas a estos objetos.

    class UserService {
        func fetchUsers(completion: @escaping ([User]?, Error?) -> Void) {
            // Lógica de solicitud de red
        }
    }
    
    class ProfileViewController: UIViewController {
        private let userService = UserService()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            userService.fetchUsers { users, error in
                // Actualización de la UI basada en los datos recibidos o errores
            }
        }
    }
    
  • Uso de Protocolos Data Source y Delegate: Implementar los protocolos UITableViewDataSource, UICollectionViewDataSource, UITableViewDelegate, UICollectionViewDelegate en clases separadas, no en el controlador mismo. El controlador solo tendrá instancias de estas clases.

    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
            // Carga de datos y pasar a dataSource.update(users:)
        }
    }
    
  • Aplicación del Patrón Coordinator: Crear objetos separados responsables de la navegación entre pantallas. Los controladores solo saben que deben ir a la siguiente pantalla, pero no qué controlador será el siguiente.

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

Estas aproximaciones permiten aliviar la carga del controlador, haciéndolo más "delgado" y enfocado en gestionar el ciclo de vida de la Vista y la coordinación entre Model y View, en lugar de ejecutar toda la lógica de la aplicación.