Sobes.tech
Middle+

Como usar a arquitetura MVC para evitar sobrecarregar o controlador?

sobes.tech IA

Resposta da IA

MVC (Model-View-Controller) no iOS frequentemente leva a controladores "pesados" devido à colocação de lógica de negócio e lógica de apresentação neles. Para resolver este problema, podem-se usar as seguintes abordagens:

  • Externalizar a lógica de apresentação na View: Criar UIView personalizados ou células de tabelas/coleções que sejam responsáveis por mostrar dados e lidar com ações básicas do utilizador (por exemplo, pressionar botões dentro da célula). O controlador apenas configura a View com os dados necessários.

    class MyCustomView: UIView {
        private let label = UILabel()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            addSubview(label)
            // Configuração de constraints, etc.
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func configure(with text: String) {
            label.text = text
        }
    
        // Manipulação de eventos UIControlEvents, por exemplo, via addTarget
    }
    
  • Externalizar a lógica de negócio em Modelos ou objetos de Serviço separados: Criar classes responsáveis por operações específicas (por exemplo, trabalho com rede, base de dados, validação de dados). O controlador delega a execução dessas tarefas a esses objetos.

    class UserService {
        func fetchUsers(completion: @escaping ([User]?, Error?) -> Void) {
            // Lógica de requisição de rede
        }
    }
    
    class ProfileViewController: UIViewController {
        private let userService = UserService()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            userService.fetchUsers { users, error in
                // Atualização da UI com base nos dados recebidos ou erro
            }
        }
    }
    
  • Utilização de Protocolos Data Source e Delegate: Implementar os protocolos UITableViewDataSource, UICollectionViewDataSource, UITableViewDelegate, UICollectionViewDelegate em classes separadas, e não no controlador. O controlador apenas terá instâncias dessas 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
            // Carregamento de dados e passagem para dataSource.update(users:)
        }
    }
    
  • Aplicação do Padrão Coordinator: Criar objetos separados responsáveis pela navegação entre telas. Os controladores apenas sabem que precisam passar para o próximo passo, mas não qual controlador será o seguinte.

    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()
            // Configuração do delegate, dataSource, etc.
            navigationController.pushViewController(usersVC, animated: false)
        }
    
        func showUserDetails(user: User) {
            let userDetailsVC = UserDetailsViewController(user: user)
            navigationController.pushViewController(userDetailsVC, animated: true)
        }
    }
    

Estas abordagens permitem descarregar o controlador, tornando-o mais "fino" e focado na gestão do ciclo de vida da View e na coordenação entre Model e View, e não na execução de toda a lógica da aplicação.