HandsAppMVP: iOS-Architektur für das Outsourcing der Studioentwicklung

Bild

Guter Code beginnt mit der Architektur, und iOS-Apps sind keine Ausnahme. Es gibt viele Standardmuster, aber der Zweck dieses Artikels besteht nicht darin, über sie zu sprechen, sondern über die Erfahrung, eines davon anzupassen und eigene zu entwickeln. Wir haben diese Anpassung HandsAppMVP genannt.

Bild

In der iOS-Entwicklung bestimmt die Architektur hauptsächlich die Organisation von Klassen und Abhängigkeiten für einen bestimmten ViewController. Die zentrale Komponente kann jedoch nicht nur er, sondern einfach UIView sein. Die Auswahl hängt von der jeweiligen Aufgabe ab.

Architekturvergleich


Es gibt verschiedene Standard-Architekturvorlagen für iOS: MVC, MVP, MVVM, VIPER (Links zur Beschreibung der einzelnen Vorlagen finden Sie am Ende des Artikels).

Bei der Auswahl einer Architektur für die Entwicklung haben wir die Hauptparameter identifiziert, denen sie entsprechen sollten: Entwicklungsgeschwindigkeit, Flexibilität und eine niedrige Eintrittsschwelle. Als Nächstes haben wir begonnen, drei bekannte Architekturen unter Berücksichtigung dieser Parameter zu vergleichen (die Vorlage der MVC iOS-Community wurde lange Zeit aufgrund der groben Nichteinhaltung der Einzelverantwortung vergraben).

Für ein Outsourcing-Team ist die Entwicklungsgeschwindigkeit besonders wichtig. VIPER ist die komplexeste und „langsamste“ Architektur. Die Entwicklung ist mit reinem MVP oder MVVM schneller, da sie weniger Komponenten enthalten.

Flexibilität bedeutet das schmerzlose Hinzufügen oder Entfernen von Funktionen in der Anwendung. Dieser Parameter korreliert stark mit der Entwicklungsgeschwindigkeit in allen Phasen der Anwendungslebensdauer, mit Ausnahme der ersten. Flexibilität hängt auch eng mit der Einfachheit des Testens zusammen - automatische Tests geben dem Entwickler das Vertrauen, dass er nichts kaputt macht, und helfen, Fehler zu vermeiden. Klassisches MVP wird von Tests nur unzureichend abgedeckt, insbesondere wenn Sie die unten beschriebenen Klassenschnittstellen nicht verwenden. MVVM hat aus Sicht des Testens auch eine schlechte Leistung, da das Testen von reaktivem Code viel länger dauert. VIPER eignet sich hervorragend zum Schreiben von Tests, da es das Prinzip der alleinigen Verantwortung so weit wie möglich respektiert und der Unterricht von Abstraktionen abhängt.

Und der letzte Parameter, den wir berücksichtigt haben, ist die Eintrittsschwelle. Es zeigt, wie schnell neue Entwickler (vor allem die Jones) in die Architektur eindringen. Hier nimmt MVVM, das reaktive Bibliotheken von Drittanbietern (RxSwift, PromiseKit usw.) verwendet, aus offensichtlichen Gründen einen ehrenvollen letzten Platz ein. VIPER ist aufgrund der großen Anzahl von Komponenten auch eine ziemlich komplexe Architektur. MVP hat die niedrigste Eintrittsschwelle.

Nachdem wir die Vor- und Nachteile abgewogen hatten, kamen wir zu dem Schluss, dass wir etwas so Einfaches wie MVP und so Flexibles wie VIPER brauchen. So entstand die Idee, auf dieser Grundlage eine eigene Architektur zu erstellen - HandsAppMVP.

Erweitern Sie MVP


Die Hauptkomponenten unserer Architektur sind Model, View, Presenter. Sie erfüllen die gleichen Funktionen wie im klassischen MVP nach dem bekannten Schema:

Bild
[Schema des klassischen MVP]

Hier und unten ist jede Interaktionskomponente (blaues Quadrat) in den Diagrammen eine Klasse, deren Lebensdauer mit der Lebensdauer der Ansicht übereinstimmt. Ein durchgezogener Pfeil zeigt den Besitz eines anderen Objekts an, ein starkes Glied, und ein gepunkteter Pfeil zeigt ein schwaches Glied an. Mit schwachen Referenzen verhindern wir zirkuläre Abhängigkeiten und Speicherlecks.

Schnittstellen


Zuerst haben wir diesem klassischen Schema die Schnittstellen ViewInput und ViewOutput hinzugefügt. Wir haben das fünfte Prinzip von SOLID berücksichtigt - das Prinzip der Abhängigkeitsinversion. Es ist eher keine Ergänzung, sondern eine Verfeinerung für MVP. Die Abhängigkeit von Abstraktionen hilft dabei, die strikte Vernetzung von Komponenten zu beseitigen, und ermöglicht es Ihnen, Tests normal zu schreiben. Das Schema unter Berücksichtigung der Schnittstellen sieht folgendermaßen aus:

Bild
[Hinzufügen von ViewInput- und ViewOutput-Schnittstellen] Ein

kleines Rechteck ist eine Schnittstelle.

Ein aufmerksamer Entwickler wird fragen, wo sich die Schnittstellen für Model befinden. Jetzt wenden wir uns an sie.

Mit Daten arbeiten


Das Datenmodell in mobilen Architekturen ist ein kollektives Konzept. Ein Standardbeispiel: Eine Anwendung klopft an das Netzwerk, um mit dem Server zu interagieren, speichert dann Daten in CoreData für die Offline-Arbeit, schreibt einige einfache Informationen in UserDefaults und speichert das JWT im Schlüsselbund. Alle diese Daten, mit denen interagiert wird, bilden das Modell.

Die Klasse, die für die Interaktion mit dem Datencontainer eines bestimmten Typs verantwortlich ist, wird als Datendienst bezeichnet. Für jeden Container (entfernte Datenbank, lokale Datenbank, UserDefaults usw.) wird HandsAppMVP eine Serviceklasse hinzugefügt, die mit dem Präsentator interagiert. Jetzt können Sie auch Eingabe- / Ausgabeschnittstellen für jeden Datendienst hinzufügen:

Bild
[Hinzufügen von Diensten zum Arbeiten mit Daten]

Nicht jede Serviceklasse muss über eine Schnittstelle mit dem Präsentator verbunden sein, wie beispielsweise bei Verwendung von Moya. Moya ist eine Open-Source-Netzwerkbibliothek. Moya bietet eine vorgefertigte Serviceklasse (MoyaProvider). Beim Schreiben von Tests müssen wir kein Scheinobjekt erstellen, das ApiProvider ersetzt. Moya bietet einen speziellen Testmodus. Wenn MoyaProvider aktiviert ist, klopft es nicht an das Netzwerk, sondern gibt Testdaten zurück (weitere Details finden Sie hier). In diesem Fall bezieht sich der Präsentator nicht auf die MoyaProvider-Abstraktion, sondern auf die Implementierung. Und wir erhalten Feedback von diesem Service über Verschlüsse. Eine Beispielimplementierung finden Sie im Demo-Projekt.

Dieses Beispiel ist eher eine Ausnahme als eine Regel und zeigt, dass die Einhaltung von SOLID nicht immer die beste Lösung ist.

Navigation


Wir betrachten die Navigation in der Anwendung als separate Verantwortung. HandsAppMVP verwendet dafür eine spezielle Klasse - Router. Der Router enthält eine schwache Verbindung zur Ansicht, mit der er einen neuen Bildschirm anzeigen oder den aktuellen schließen kann. Der Router interagiert auch mit dem Präsentator über die RouterInput-Schnittstelle:

Bild
[Hinzufügen einer Komponente für die Navigation (Router)]

Komponentenmontage


Die letzte Ergänzung zum klassischen MVP, die wir verwenden, ist Assembly, eine Sammlerklasse. Es wird verwendet, um die Ansicht und andere Komponenten von HandsAppMVP zu initialisieren sowie Abhängigkeiten zu implementieren. Assembly enthält die einzige öffentliche Methode - `assemble () -> UIViewController`, deren Ergebnis der gewünschte UIViewController (oder UIView) mit dem erforderlichen Abhängigkeitsgraphen ist.

Assembly wird im Architekturdiagramm nicht angezeigt, da es nicht mit MVP-Komponenten verbunden ist und sein Lebenszyklus unmittelbar nach ihrer Erstellung endet.

Codegenerierung


Um Zeit zu sparen, haben wir den Prozess der Erstellung von HandsAppMVP-Klassen mit Generamba automatisiert. Die für Generamba verwendeten Vorlagen finden Sie in unserem Repository. Eine Beispielkonfiguration für Generamba befindet sich im Demo-Projekt.

Durch das Generieren eines bestimmten Bildschirms erhalten wir eine Reihe von Klassen, die dem HandsAppMVP-Schema entsprechen, eine Reihe von Komponententests zum Erstellen und Implementieren von Komponenten sowie eine Vorlagenklasse für Presenter-Tests.

Was ist passiert


Wenn Sie Head-to-Head HandsAppMVP und VIPER vergleichen, werden Sie feststellen, dass sie sehr ähnlich sind und die erste nur durch das Fehlen der Interactor-Komponente gekennzeichnet ist. Indem wir jedoch die Schicht zwischen den Diensten und der Gegenwart (dem Interaktor) loswurden und die Interaktion mit dem Netzwerk mithilfe von Moya vereinfachten, konnten wir die Entwicklungsgeschwindigkeit spürbar steigern.

Wir empfehlen Ihnen, der Architektur in der Entwurfsphase genügend Aufmerksamkeit zu schenken, um globale Fehler, Streitigkeiten mit Kunden und Qualen von Entwicklern in Zukunft zu vermeiden und stattdessen den Entwicklungsprozess kompetent und vorhersehbar zu führen.

Denken Sie daran, dass eine Architektur möglicherweise nicht speziell für Ihr Projekt geeignet ist. Halten Sie sich also nicht blind an vorgefertigte Vorlagen und Erfolgsgeschichten ihrer Anwendung. Haben Sie keine Angst, Ihre Lösungen zu entwickeln und anzuwenden - sie können für Sie wertvoller und flexibler werden als vorgefertigte.

Abschließend werden wir einige gute Artikel zur Architektur von iOS-Anwendungen empfehlen, die uns geholfen haben, die Feinheiten zu verstehen und eine Auswahl zu treffen:

  1. Architekturmuster in iOS
  2. iOS Swift: MVP-Architektur
  3. Analyse der VIPER-Architektur am Beispiel einer kleinen iOS-Anwendung unter Swift 4
  4. Implementieren Sie MVVM unter iOS mit RxSwift

Die Open-Source-Dokumentation von SurfStudio war ebenfalls sehr hilfreich und inspiriert .

Schließlich setzen wir einen Link auf das in HandsAppMVP geschriebene DEMO-Projekt , das wir im Artikel wiederholt erwähnt haben.

All Articles