Sobes.tech
Middle+

Wie man die MVC-Architektur nutzt, um eine Überlastung des Controllers zu vermeiden?

sobes.tech KI

Antwort von AI

MVC (Model-View-Controller) in iOS führt häufig zu "dicken" Controllern, weil die Geschäftslogik und Präsentationslogik darin platziert werden. Um dieses Problem zu lösen, können folgende Ansätze verwendet werden:

  • Auslagerung der Präsentationslogik in die View: Erstellen von benutzerdefinierten UIView oder Tabellen-/Sammlungszellen, die für die Anzeige der Daten und die Verarbeitung grundlegender Benutzeraktionen (z.B. Button-Klicks innerhalb der Zelle) verantwortlich sind. Der Controller konfiguriert nur die View mit den erforderlichen Daten.

    class MyCustomView: UIView {
        private let label = UILabel()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            addSubview(label)
            // Constraints und weitere Konfiguration
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func configure(with text: String) {
            label.text = text
        }
    
        // Behandlung von UIControlEvents, z.B. via addTarget
    }
    
  • Auslagerung der Geschäftslogik in Models oder separate Service-Objekte: Erstellen von Klassen, die für spezifische Operationen verantwortlich sind (z.B. Netzwerk, Datenbank, Validierung). Der Controller delegiert die Ausführung dieser Aufgaben an diese Objekte.

    class UserService {
        func fetchUsers(completion: @escaping ([User]?, Error?) -> Void) {
            // Netzwerk-Logik
        }
    }
    
    class ProfileViewController: UIViewController {
        private let userService = UserService()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            userService.fetchUsers { users, error in
                // UI-Update basierend auf den empfangenen Daten oder Fehler
            }
        }
    }
    
  • Verwendung von Data Source und Delegate Protocols: Implementierung der Protokolle UITableViewDataSource, UICollectionViewDataSource, UITableViewDelegate, UICollectionViewDelegate in separaten Klassen, nicht im Controller selbst. Der Controller hält nur Instanzen dieser Klassen.

    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
            // Laden der Daten und Übergabe an dataSource.update(users:)
        }
    }
    
  • Anwendung des Coordinator-Patterns: Erstellen von separaten Objekten, die für die Navigation zwischen Bildschirmen verantwortlich sind. Controller wissen nur, dass sie zum nächsten Schritt wechseln müssen, aber nicht, welcher Controller als nächstes folgt.

    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()
            // Konfiguration des Delegierten, dataSource usw.
            navigationController.pushViewController(usersVC, animated: false)
        }
    
        func showUserDetails(user: User) {
            let userDetailsVC = UserDetailsViewController(user: user)
            navigationController.pushViewController(userDetailsVC, animated: true)
        }
    }
    

Diese Ansätze ermöglichen es, den Controller zu entlasten, ihn "dünner" zu machen und ihn auf die Verwaltung des View-Lebenszyklus und die Koordination zwischen Model und View zu konzentrieren, anstatt die gesamte Logik der Anwendung auszuführen.