Hallo Leser!
In einem
früheren Artikel habe ich über den
VIP- Zyklus der
Clean Swift- Architektur gesprochen. Jetzt werden wir eines der wichtigsten Themen ansprechen - den
Übergang und die
Übertragung von Daten zwischen Szenen.

Theorie
Die
Router- Komponente ist für die Navigations- und Datenübertragungslogik verantwortlich, die Teil der Szene ist (natürlich optional). Es wird im
ViewController zusammen mit dem
Interactor und dem
Presenter initialisiert.
Der Router implementiert zwei Protokolle -
RoutingLogic und
DataPassing , die wir mit unserer Funktionalität füllen werden.
RoutingLogic sollte die Methoden enthalten, die für den Übergang zu einer bestimmten Szene verantwortlich sind.
DataPassing enthält die Variable
dataStore , die auf den Protocol
DataStore verweist .
Interactor Scenes implementiert das
DataStore- Protokoll und arbeitet mit den darin gespeicherten Variablen.
Der Router selbst enthält einen Link zum
ViewController seiner Szene.
Über den Link zum
ViewController springt der
Router zwischen den Szenen. Dazu können Sie
Segue verwenden oder programmgesteuert eine Szene erstellen, zu der Sie einen Übergang vornehmen möchten. Welche Methode verwendet wird, ist nicht wichtig. Für uns ist es wichtig, einen Link zu einer Instanz der
ViewController- Klasse zu haben, zu der wir wechseln.
Über den Link zum
DataStore übertragen wir Daten vom
Interactor einer Szene zum
Interactor der Szene, zu der wir wechseln. Und wie bereits erwähnt, muss der
Router wissen, wie das geht.
Übe
Zum Beispiel übertragen wir den Text von TextField auf Label einer anderen Szene. Betrachten wir zwei Arten des Übergangs zwischen Szenen - nach
Segue und programmgesteuert.
Die
Router- Klasse enthält 3 semantische Gruppen von Methoden:
- Methoden aus der RoutingLogic- Implementierung (routeTo)
- Navigationsmethoden (navigieren zu, Übergang ohne Segue)
- Methoden zur Datenübertragung (passDataTo, wenn Daten zur Übertragung vorhanden sind)

Wenn wir beispielsweise beim
Klicken auf eine Schaltfläche einen Übergang durch
Segue vornehmen , müssen wir in
ViewController die Methode prepare (for: sender :) überschreiben. Mit dieser Erweiterung können Sie automatisch Methoden vom
Router mit dem Namen
Segue aufrufen.
Das Überschreiben der Vorbereitung (für: Absender :) ist optional, wenn Sie mit Segue arbeiten. Sie können es aus dem Code ausschließen und performSegue (withIdentifier: sender :) in der Router- Methode aufrufen . Die Vorbereitung ist nur erforderlich, wenn Sie Segue zusammen mit der Datenübertragung verwenden müssen. | final class HomeViewController: UIViewController { |
| // ... |
| |
| override func prepare(for segue: UIStoryboardSegue, sender: Any?) { |
| // Segue |
| if let scene = segue.identifier { |
| |
| // , Router |
| // router?.routeToNAME(segue:) |
| let selector = NSSelectorFromString("routeTo\(scene)WithSegue:") |
| |
| // , |
| // Segue |
| if let router = router, router.responds(to: selector) { |
| router.perform(selector, with: segue) |
| } |
| } |
| } |
| |
| // ... |
| } |
Jetzt kommen wir endlich zu dem interessantesten - dem
Router- Code. Das Beispiel enthält Kommentare, daher werden nur wichtige Punkte berücksichtigt.
In diesem
Router arbeiten wir mit zwei Szenen -
Home und
Detail . Der Übergang von der
Home- Szene erfolgt ebenfalls auf zwei Arten - von
Segue und
programmgesteuert . Daten werden von der
Home- Szene in die
Detail- Szene übertragen.
Alle Methoden im
RoutingLogic- Protokoll müssen nach dem
routeToNAME- Prinzip benannt werden, wobei
NAME der Name des
Segues (Bezeichners) ist, den wir bei der Arbeit mit dem
Storyboard angeben. Dies ist nicht nur für die Benutzerfreundlichkeit und Schönheit der Verwendung erforderlich, sondern auch für unseren Methodenselektor zur
Vorbereitung (für: Absender :) ViewController , den wir zuvor neu definiert haben.
Auch in der
HomeRouter- Klasse
gibt es Methoden, die mit
navigTo und
passDataTo beginnen . Die ersteren sind für die Übergangslogik verantwortlich, während die letzteren für die Datenübertragung verantwortlich sind. Die navigateTo-Methoden werden nur erstellt, wenn der Übergang programmgesteuert erfolgt.
Im Beispiel haben wir eine
routeToDetail (segue :) -Methode. Der Parameter
segue ist seitdem optional Die Methode enthält eine Implementierung, mit der Sie sie ohne Verwendung von
Segue aufrufen können. In beiden Übergangsfällen erhalten wir nicht optionale Werte für unsere
HomeViewController'a- und
HomeDataStore'a- Home- Szene sowie Links zu
ViewController- und
DataStore-Detailszene . Hierbei ist zu beachten, dass
detailDS eine Variable ist und
mit dem Parameter
pass -through (inout) an die Methode passDataToDetail übergeben wird. Das ist wichtig, weil Ohne
Inout müssen wir alle
DataStore- Protokolle als "nur mit Klassen implementierbar" (Protokoll DetailDataStore: class) markieren. Dies bringt viele Schwierigkeiten mit sich, einschließlich der Erfassung starker Links.
| |
| import UIKit |
| |
| /// @objc |
| /// prepare View Controller'e |
| @objc protocol HomeRoutingLogic { |
| /// Detail View Controller |
| func routeToDetail(segue: UIStoryboardSegue?) |
| } |
| |
| protocol HomeDataPassing { |
| var dataStore: HomeDataStore? { get } |
| } |
| |
| final class HomeRouter: NSObject, HomeRoutingLogic, HomeDataPassing { |
| |
| // MARK: - Private |
| |
| // MARK: - Public |
| |
| weak var viewController: HomeViewController? |
| var dataStore: HomeDataStore? |
| |
| // MARK: - HomeRoutingLogic |
| |
| func routeToDetail(segue: UIStoryboardSegue?) { |
| if let segue = segue { |
| // |
| // Segue |
| |
| // Detail View Controller |
| // Data Store Router'e |
| guard |
| let homeDS = dataStore, |
| let detailVC = segue.destination as? DetailViewController, |
| var detailDS = detailVC.router?.dataStore |
| else { fatalError("Fail route to detail") } |
| |
| // , , , |
| // "" |
| passDataToDetail(source: homeDS, destination: &detailDS) |
| } else { |
| // , |
| // Segue |
| |
| // Detail View Controller Storyboard'a |
| // Data Store Router'e |
| guard |
| let viewController = viewController, |
| let homeDS = dataStore, |
| let storyboard = viewController.storyboard, |
| let detailVC = storyboard.instantiateViewController(withIdentifier: "Detail") as? DetailViewController, |
| var detailDS = detailVC.router?.dataStore |
| else { fatalError("Fail route to detail") } |
| |
| passDataToDetail(source: homeDS, destination: &detailDS) |
| |
| // , "" |
| navigateToDetail(source: viewController, destination: detailVC) |
| } |
| } |
| |
| // MARK: - Navigation |
| |
| private func navigateToDetail(source: HomeViewController, destination: DetailViewController) { |
| source.navigationController?.pushViewController(destination, animated: true) |
| } |
| |
| // MARK: - Passing data |
| |
| /// destination inout, |
| /// Data Store |
| private func passDataToDetail(source: HomeDataStore, destination: inout DetailDataStore) { |
| |
| // HomeDataStore DetailDataStore |
| destination.message = source.message |
| } |
| } |
Fazit
Das ist alles. Vielen Dank für das Lesen bis zum Ende! Unten werde ich einen Link zum Projekt hinterlassen, wenn Sie den Artikel in Aktion ausprobieren möchten.
Artikelserie
- Übersicht über die saubere Swift-Architektur
- Router- und Datenübergabe in Clean Swift-Architektur (Sie sind hier)
- Arbeiter sauberer schneller Architektur
- Unit-Tests in der Clean Swift-Architektur
- Ein Beispiel für eine einfache Online-Shop-Architektur Clean Swift
Link zum ProjektHilfe beim Schreiben eines Artikels:
Bastien