Übersicht über die saubere Swift-Architektur

Hallo Leser!

In diesem Artikel werde ich über die Architektur von iOS-Anwendungen sprechen - Clean Swift . Wir werden die wichtigsten theoretischen Punkte betrachten und ein Beispiel in der Praxis analysieren.



Theorie


Zunächst werden wir die grundlegende Terminologie der Architektur analysieren. In Clean Swift besteht eine Anwendung aus Szenen, d.h. Jeder Anwendungsbildschirm ist eine Szene. Die Hauptinteraktion in der Szene durchläuft eine sequentielle Schleife zwischen den Komponenten von ViewController -> Interactor -> Presenter . Dies wird als VIP- Zyklus bezeichnet.

Die Brücke zwischen den Komponenten ist die Modelldatei , in der die übertragenen Daten gespeichert werden. Es gibt auch einen Router , der für die Übertragung und Übertragung von Daten zwischen Szenen verantwortlich ist, und Worker , der einen Teil der Interactor- Logik übernimmt.



Anzeigen


Storyboards, XIBs oder UI-Elemente, die über Code geschrieben wurden.

ViewController


Verantwortlich nur für die Konfiguration und Interaktion mit View . Der Controller sollte keine Geschäftslogik, Netzwerkinteraktionen, Computer usw. enthalten.
Seine Aufgabe ist es, Ereignisse mit Anzeigen, Anzeigen oder Senden von Daten (ohne Verarbeitung und Überprüfung) in Interactor zu verarbeiten .

Interactractor


Es enthält die Geschäftslogik der Szene.

Es funktioniert mit den Netzwerk-, Datenbank- und Gerätemodulen.

Interactor empfängt eine Anfrage von ViewController (mit oder ohne Daten), verarbeitet sie und überträgt bei Bedarf neue Daten an Presenter .

Moderator


Er ist mit der Aufbereitung von Daten für die Anzeige beschäftigt.

Fügen Sie beispielsweise einer Telefonnummer eine Maske hinzu oder machen Sie den ersten Buchstaben in der Großbuchstaben.
Es verarbeitet die von Interactor empfangenen Daten und sendet sie dann an den ViewController zurück .

Modelle


Eine Reihe von Strukturen zum Übertragen von Daten zwischen den Komponenten des VIP- Zyklus. Jeder Kreis des Zyklus hat 3 Arten von Strukturen:

  • Anfrage - Datenstruktur (Text aus TextField usw.) zur Übertragung von ViewController an Interactor
  • Antwort - Struktur mit Daten (aus dem Netzwerk heruntergeladen usw.) zur Übertragung von Interactor an Presenter
  • ViewModel - Struktur mit verarbeiteten Daten (Textformatierung usw.) in Presenter für die Rückübertragung an ViewController

Arbeiter


Entlädt Interactor und übernimmt einen Teil der Geschäftslogik der Anwendung, wenn Interactor schnell wächst.

Sie können auch Worker erstellen, die allen Szenen gemeinsam sind, wenn ihre Funktionalität in mehreren Szenen verwendet wird.

In Worker können Sie beispielsweise die Logik für die Arbeit mit einem Netzwerk oder einer Datenbank festlegen.

Router


Die gesamte Logik, die für Übergänge und Datenübertragung zwischen Szenen verantwortlich ist, wird im Router entfernt .



Um das Bild des VIP- Zyklus zu verdeutlichen, gebe ich ein Standardbeispiel - die Autorisierung.

  1. Der Benutzer gab seinen Benutzernamen und sein Passwort ein und klickte auf die Autorisierungsschaltfläche
  2. Der ViewController löst eine IBAction aus. Anschließend wird eine Struktur mit den in TextFields eingegebenen Benutzerdaten erstellt (Models -> Request).
  3. Die erstellte Struktur wird in Interactor'e an die Methode fetchUser übergeben
  4. Interactor sendet eine Anfrage an das Netzwerk und erhält eine Antwort über den Erfolg der Autorisierung
  5. Erstellt basierend auf den empfangenen Daten eine Struktur mit dem Ergebnis (Modelle -> Antwort) und wird in Presenter'e an die Methode presentUser übergeben
  6. Presenter formatiert die Daten nach Bedarf und gibt sie (Modelle -> ViewModel) an die displayUser- Methode in ViewController'e zurück
  7. ViewController zeigt dem Benutzer die empfangenen Daten an. Im Falle einer Autorisierung kann ein Fehler angezeigt oder ein Übergang zu einer anderen Szene über den Router ausgelöst werden

So erhalten wir eine einheitliche und konsistente Struktur mit der Verteilung der Verantwortlichkeiten auf die Komponenten.

Übe


Schauen wir uns nun ein kleines praktisches Beispiel an, das zeigt, wie der VIP- Zyklus verläuft. In diesem Beispiel simulieren wir das Laden von Daten beim Öffnen einer Szene (Bildschirm). Ich habe die Hauptabschnitte des Codes mit Kommentaren markiert.

Der gesamte VIP- Zyklus ist an Protokolle gebunden, sodass alle Module ausgetauscht werden können, ohne die Anwendung zu stören.
Für ViewController wird ein DisplayLogic- Protokoll erstellt, dessen Link zum späteren Abruf an Presenter übergeben wird . Für Interactor werden zwei BusinessLogic- Protokolle erstellt, die für den Aufruf von Methoden aus ViewController und DataSource verantwortlich sind , um Daten zu speichern und eine andere Szene über den Router an Interactor zu übertragen . Presenter abonniert das PresentationLogic- Protokoll zum Aufrufen von Interactor . Das verbindende Element all dessen sind Modelle . Es enthält Strukturen, mit deren Hilfe Informationen zwischen den Komponenten des VIP- Zyklus ausgetauscht werden. Wir beginnen damit die Code-Analyse.



Modelle


Im folgenden Beispiel habe ich für die Home- Szene eine HomeModels- Datei erstellt, die eine Reihe von Abfragen für die VIP- Schleife enthält.
Die FetchUser- Anforderung ist für das Laden von Benutzerdaten verantwortlich, die wir weiter betrachten werden.

// Models
/// VIP
enum HomeModels {
/// VIP
enum FetchUser {
/// Interactor View Controller
struct Request {
let userName: String
}
/// Presentor Interactor
struct Response {
let userPhone: String
let userEmail: String
}
/// View Controller Presentor
struct ViewModel {
let userPhone: String
let userEmail: String
}
}
}

ViewController


Wenn die Klasse initialisiert wird, instanziieren wir die Interactor- und Presenter- Klassen dieser Szene und stellen Abhängigkeiten zwischen ihnen her.
Weiter in ViewController'e gibt es nur einen Link zu Interactor . Über diesen Link erstellen wir eine Anfrage an die Methode fetchUser (request :) in Interactor , um den VIP- Zyklus zu starten.

Hier lohnt es sich zu beachten, wie die Anfrage an Interactor erfolgt. In der loadUserInfromation () -Methode erstellen wir eine Instanz der Request- Struktur, in der wir den Anfangswert übergeben. Es kann aus TextField , Tabellen usw. entnommen werden. Eine Instanz der Anforderungsstruktur wird an die Methode fetchUser (request :) übergeben , die sich im BusinessLogic- Protokoll unseres Interactors befindet .

// ViewController
///
protocol HomeDisplayLogic: class {
///
func displayUser(_ viewModel: HomeModels.FetchUser.ViewModel)
}
final class HomeViewController: UIViewController {
/// Interactor'a
var interactor: HomeBusinessLogic?
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
///
private func setup() {
// VIP
let interactor = HomeInteractor()
let presenter = HomePresenter()
//
interactor.presenter = presenter
presenter.viewController = self
// Interactor View Controller
self.interactor = interactor
}
override func viewDidLoad() {
super.viewDidLoad()
//
fetchUser()
}
/// Interactor
private func loadUserInfromation() {
// Interactor
let request = HomeModels.FetchUser.Request(userName: "Aleksey")
// Interactor'a
interactor?.fetchUser(request)
}
}
/// HomeDisplayLogic
extension HomeViewController: HomeDisplayLogic {
func displayUser(_ viewModel: HomeModels.FetchUser.ViewModel) {
print(viewModel)
}
}

Interactractor


Eine Instanz der Interactor- Klasse enthält einen Link zum PresentationLogic- Protokoll, unter dem Presenter signiert ist.

Die Methode fetchUser (request :) kann eine beliebige Datenladelogik enthalten. Zum Beispiel habe ich gerade Konstanten mit angeblich erhaltenen Daten erstellt.

Bei derselben Methode wird eine Instanz der Antwortstruktur erstellt und mit den zuvor erhaltenen Parametern gefüllt. Die Antwort wird mit der Methode presentUser (response :) an PresentationLogic übergeben . Mit anderen Worten, hier haben wir die Rohdaten erhalten und zur Verarbeitung an Presenter übergeben .

// Interactor
/// Interactor'a
protocol HomeBusinessLogic: class {
///
func fetchUser(_ request: HomeModels.FetchUser.Request)
}
final class HomeInteractor: HomeBusinessLogic {
///
var presenter: HomePresentationLogic?
func fetchUser(_ request: HomeModels.FetchUser.Request) {
//
//
let userPhone = "+7 (999) 111-22-33"
let userEmail = "im@alekseypleshkov.ru"
// ...
// Presentor'
let response = HomeModels.FetchUser.Response(userPhone: userPhone, userEmail: userEmail)
// Presentor'
presenter?.presentUser(response)
}
}

Moderator


Es verfügt über einen Link zum DisplayLogic- Protokoll, unter dem ViewController signiert ist. Es enthält keine Geschäftslogik, sondern formatiert nur die empfangenen Daten, bevor sie angezeigt werden. Im Beispiel haben wir die Telefonnummer formatiert, eine Instanz der ViewModel- Struktur vorbereitet und sie mit der Methode displayUser (viewModel :) im DisplayLogic- Protokoll an den ViewController übergeben , in der bereits Daten angezeigt werden.

///
protocol HomePresentationLogic: class {
/// Interactor'a
func presentUser(_ response: HomeModels.FetchUser.Response)
}
final class HomePresenter: HomePresentationLogic {
/// View Controller'a
weak var viewController: HomeDisplayLogic?
func presentUser(_ response: HomeModels.FetchUser.Response) {
//
let formattedPhone = response.userPhone.replacingOccurrences(of: "-", with: " ")
// ViewModel View Controller
let viewModel = HomeModels.FetchUser.ViewModel(userPhone: formattedPhone, userEmail: response.userEmail)
// View Controller'a
viewController?.displayUser(viewModel)
}
}

Fazit


Mit dieser Architektur hatten wir die Möglichkeit, Verantwortlichkeiten zu verteilen, das Testen der Anwendung zu vereinfachen, die Austauschbarkeit einzelner Abschnitte der Implementierung und den Standard für das Schreiben von Code für die Arbeit in einem Team einzuführen.

Vielen Dank für das Lesen bis zum Ende.

Artikelserie


  1. Grundlegendes zur Clean Swift-Architektur (Sie sind hier)
  2. Router- und Datenübergabe in einer sauberen Swift-Architektur
  3. Arbeiter sauberer schneller Architektur
  4. Unit-Tests in der Clean Swift-Architektur
  5. Ein Beispiel für eine einfache Online-Shop-Architektur Clean Swift

Alle Komponenten der Szene: Link
Hilfe beim Schreiben eines Artikels: Bastien

Source: https://habr.com/ru/post/de453986/


All Articles