Jak używać architektury MVC, aby uniknąć przeciążenia kontrolera?
sobes.tech AI
Odpowiedź od AI
MVC (Model-View-Controller) w iOS często prowadzi do "grubych" kontrolerów z powodu umieszczania w nich logiki biznesowej i logiki prezentacji. Aby rozwiązać ten problem, można zastosować następujące podejścia:
-
Przeniesienie logiki prezentacji do View: Tworzenie niestandardowych
UIViewlub komórek tabel/zbiorów, które odpowiadają za wyświetlanie danych i obsługę podstawowych działań użytkownika (np. naciśnięcia przycisków w komórce). Kontroler jedynie konfiguruje View odpowiednimi danymi.class MyCustomView: UIView { private let label = UILabel() override init(frame: CGRect) { super.init(frame: frame) addSubview(label) // Konfiguracja constraints itp. } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(with text: String) { label.text = text } // Obsługa zdarzeń UIControlEvents, np. przez addTarget } -
Przeniesienie logiki biznesowej do Modeli lub oddzielnych obiektów Service: Tworzenie klas odpowiedzialnych za konkretne operacje (np. praca z siecią, bazą danych, walidacją danych). Kontroler deleguje wykonanie tych zadań tym obiektom.
class UserService { func fetchUsers(completion: @escaping ([User]?, Error?) -> Void) { // Logika zapytania sieciowego } } class ProfileViewController: UIViewController { private let userService = UserService() override func viewDidLoad() { super.viewDidLoad() userService.fetchUsers { users, error in // Aktualizacja UI na podstawie otrzymanych danych lub błędu } } } -
Użycie protokołów Data Source i Delegate: Implementacja protokołów
UITableViewDataSource,UICollectionViewDataSource,UITableViewDelegate,UICollectionViewDelegatew osobnych klasach, a nie w samym kontrolerze. Kontroler będzie miał tylko instancje tych klas.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 // Wczytanie danych i przekazanie do dataSource.update(users:) } } -
Zastosowanie wzorca Coordinator: Tworzenie osobnych obiektów odpowiedzialnych za nawigację między ekranami. Kontrolery wiedzą tylko, że muszą przejść do następnego kroku, ale nie wiedzą, który kontroler będzie następny.
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() // Konfiguracja delegata, dataSource itp. navigationController.pushViewController(usersVC, animated: false) } func showUserDetails(user: User) { let userDetailsVC = UserDetailsViewController(user: user) navigationController.pushViewController(userDetailsVC, animated: true) } }
Te podejścia pozwalają odciążyć kontroler, czyniąc go "cieńszym" i skupiającym się na zarządzaniu cyklem życia View oraz koordynacji między Modelem a View, a nie na wykonywaniu całej logiki aplikacji.