Wenn Sie ein Entwickler mobiler Apps sind, haben Sie vielleicht von der Flexibilität der Online-Entwicklung geträumt, Layout- und Logikänderungen spontan vorzunehmen, und von der Möglichkeit, Hypothesentests in Sekundenschnelle durchzuführen und Ergebnisse noch schneller zu verarbeiten?

Entwicklern von Mobilgeräten wird beigebracht zu glauben, dass die Geschwindigkeit, mit der Anwendungen veröffentlicht und aktualisiert werden, in direktem Zusammenhang damit steht, wie schnell sie bei den Benutzern ankommen. Die Moderationszeit im App Store kann frustrierend lang sein. Das Erstellen eines Software Development Kits (SDK) wird noch langsamer, da Sie Ihre Anforderungen in die Produktentwicklungs- und Veröffentlichungszyklen anderer einpassen müssen. Es kann eine gute Ausrede sein, Hypothesen überhaupt nicht zu testen.

In diesem Blogbeitrag gehen wir auf das Thema ein und führen Sie durch die Schritte der Backend-gesteuerten Entwicklung, wobei wir beschreiben, wie es zur Lösung bestimmter Probleme verwendet wird und welche Vorteile es uns gebracht hat. Die Materialien für diesen Beitrag wurden der Quelle am Beispiel von MovilePay entnommen. Der Autor des Originaltextes ist Rodrigo Maximo.

Was ist Backend-getriebene Entwicklung?

Backend-gesteuerte Entwicklung (oder Backend-gesteuerte Entwicklung oder Backend-gesteuerte Benutzeroberfläche oder servergesteuerte Benutzeroberfläche) ist das Konzept der Entwicklung von Front-End-Anwendungen auf der Grundlage von Serverantworten. Die Bildschirme und der Ablauf ändern sich basierend auf den Antworten des Servers.

Der Großteil der Arbeit beim Erstellen mobiler Anwendungen ist normalerweise mit dem Erstellen einer Benutzeroberfläche verbunden: Das Platzieren der Elemente, mit denen der Benutzer interagiert, auf Bildschirmen, damit er schnell die eine oder andere Aktion ausführen kann. Einige dieser Komponenten sind mit API-Nutzlasten gefüllt: typischerweise JSON mit primitiven Typen (Ganzzahlen, boolesche Werte, Zeichenfolgen) oder Geschäftsprozessen der Anwendung.

Der herkömmliche Ansatz zur Implementierung von Bildschirmen hat bestimmte Nachteile, da auf der Anwendungsseite viel Geschäftslogik vorhanden sein kann und der Code schwieriger zu warten ist:

  • Benötigen Sie denselben Code für viele Plattformen (Android, iOS, Web usw.). Dieser Ansatz erschwert die Aufrechterhaltung der plattformübergreifenden Kompatibilität, was die Wahrscheinlichkeit von Fehlern und Inkompatibilitäten erhöht.
  • Jede Aktualisierung oder Änderung einer mobilen Anwendung impliziert die Notwendigkeit, den Code zu ändern, was zu einer verlängerten Veröffentlichung von Anwendungen im App Store führt;
  • Im digitalen Bereich ist das A/B-Testen schwieriger. Es ist schwieriger, Konzepte zu testen und Daten von Benutzern zu sammeln, um wichtige Produktinformationen zu verstehen;
  • Da sich der Großteil der Geschäftslogik auf der Anwendungsseite befindet, ist der Code schwieriger zu pflegen und zu warten.

Backend-orientierte Entwicklung ist angekommen, um diese Probleme zu lösen.

Stellen Sie sich das folgende Szenario vor (das auf der Beispiel-iOS-App basiert, aber problemlos in jedes andere Front-End-Projekt übersetzt werden kann):

Scenario for backend driven development

 {
    "pageTitle" : "Demonstrativer Titel" ,
    "Kisten" : [
        {
            "Typ ": "bigBlue" ,
            „Titel“ : „Schöne Kiste“ ,
            „subtitle“ : „Untertitel der Box“
        } ,
        {
            "Typ ": "kleinesRot" ,
            „title“ : „Tolle Kiste“
        } ,
        {
            "Typ ": "RechteckGrün" ,
            „Titel“ : „Unglaubliche Kiste“ ,
            „Untertitel“ : „Untertitel der Box“ ,
            „Nummer“ : 10
        }
    ]
}

Die gesamte Geschäftslogik zum Anzeigen von Feldern und zum Anzeigen von Text und visuellen Informationen wird auf der Serverseite konsolidiert und abstrahiert, wodurch die Notwendigkeit mehrerer Schnittstellen zur Verarbeitung dieser API entfällt. Der Server wendet dann die Geschäftslogik an und verwendet ihre Ergebnisse, um eine API-Antwort im JSON-Format zu generieren.

Um im Beispiel Blöcke auf dem Bildschirm anzuzeigen ("bigBlue", "smallRed" und "rectangleGreen"), werden zusätzliche Informationen aus jedem Feld verwendet. Interessanterweise ermöglicht die Variable „boxes“ dem Backend, so viele Blöcke zu verarbeiten, wie der Server bereitstellt.

Haben Sie sicher schon erraten, wie Sie in dieser Situation A/B-Tests durchführen können? Der Server kann bestimmte Benutzer auswählen, um nur "bigBlue"-Blöcke anzuzeigen, während andere alle drei Arten von Blöcken sehen können. Außerdem können Sie A / B-Tests durchführen, die die Reihenfolge der Anzeige von Blöcken auf dem Bildschirm ändern.

Ein weiterer Anwendungsfall für die servergesteuerte Entwicklung besteht darin, Änderungen an der Benutzeroberfläche einer Anwendung vorzunehmen. Wenn wir beispielsweise die Header in der Anwendung ändern müssen, reicht es aus, einfach die Antwort des Servers zu ändern. Die Anzeigereihenfolge von Untertiteln und Blöcken lässt sich ebenfalls einfach ändern. In diesem Fall müssen Sie keine neue Anwendungsversion im AppStore veröffentlichen.

Making changes to the interface in backend driven development

 {
    "pageTitle" : "Demonstrativer Titel" ,
    "Kisten" : [
        {
            "Typ ": "RechteckGrün" ,
            „title“ : „Anderer Titel“ ,
            „Untertitel“ : „Ein weiterer Untertitel“ ,
            "Zahl" : 100
        } ,
        {
            "Typ ": "kleinesRot" ,
            „Titel“ : „Anderer Titel“
        }
        {
            "Typ ": "bigBlue" ,
            „Titel“ : „Backend-gesteuert“ ,
            „Untertitel“ : „Entwicklung“
        }
    ]
}

Denke nochmal nach

Bei der Verwendung des Backend-Driven-Development-Ansatzes sind einige Dinge zu beachten. Erstens, welchen Grad an Flexibilität benötigen Sie?

Basierend auf Erfahrung und Forschung halten wir eine mittlere Flexibilität für optimal.

Sie sollten nicht von einem Extrem zum anderen eilen. Ein großes Maß an Freiheit kann sich negativ auf die Amortisation Ihrer Entwicklung auswirken. Sie können nicht alles vorhersehen, zumal Gerätespezifikationen oder Bildschirmgröße außerhalb Ihrer Kontrolle liegen. Schließlich erhalten Sie eine sehr verwirrende und überladene anwendungsseitige Präsentationslogik. Außerdem können Sie nicht alles vorhersehen, was Ihr Designteam in Zukunft tun möchte. So müssen Sie immer noch Änderungen am Anwendungscode vornehmen, und beim Testen werden zahlreiche komplexe Wege zum Erreichen des Ziels entdeckt.

Zusammenfassend benötigen Sie also nicht die Flexibilität, die HTML in den allermeisten Fällen bietet. Bevor Sie Bildschirme und Serverlösungen entwickeln, die die Idee der Backend-Driven Development verwenden, empfehlen wir Ihnen daher, alle möglichen Optionen zu durchdenken.

Eine Super-App-Erstellung, Mobile Tech Case

Wahrscheinlich kennen Sie das Super-App-Konzept und haben schon von einer App wie WeChat gehört. WeChat ist eine mobile Messaging-Plattform und ein soziales Netzwerk, das in China entwickelt wurde und weltweit sehr beliebt geworden ist.

Eine solche Anwendung zielt darauf ab, mehrere wiederkehrende Dienste an einem Ort zu sammeln und den Benutzern einen einzigen Zugangspunkt zu den meisten Online-Anfragen aus ihrem täglichen Leben zu bieten. Es ist der heilige Gral der Softwareentwicklung: viele Funktionen zusammenzustellen, damit alles so aussieht, als ob es an wäree und bietet Benutzern einfache Antworten auf komplexe Fragen und Lösungen für große Probleme.

In der Vergangenheit war MovilePay an der Entwicklung einer Super-App beteiligt, deren Prozess zur Entwicklung einer verbesserten Version in diesem Artikel beschrieben wird. Es sollte ein Test- und Validierungszentrum sein, in dem das Team neue Dienste testen und Gemeinsamkeiten bei deren Verwendung finden konnte.

MovilePay hatte ein Problem bei der Erstellung einer Anwendung, die schnell Änderungen am Code vornehmen konnte. Es erforderte das Testen vieler Dienste und Dienste und das Durchführen von Untersuchungen zum Benutzerverhalten auf der Grundlage der angezeigten Informationen und das Testen von Hypothesen. MovilePay wollte Funktionen schnell ein- und ausschalten können. Unter solchen Bedingungen war es unmöglich, den traditionellen Ansatz mit einer Freigabe für jede Änderung zu verwenden. Unter Berücksichtigung all dieser Punkte und eines komplexen Szenarios entschied man sich für Backend-Driven Development.

MovilePay hat einen kategoriebasierten Startbildschirm erstellt, um dieses Problem zu lösen. Jeder Abschnitt hat seinen eigenen Satz von Elementen, die als Widgets bezeichnet werden. Abschnitte zeigten Einstiegspunkte für bereits implementierte Dienste in beliebiger Reihenfolge und hoben einzelne Widgets hervor.

Manchmal wurde nur ein Dienst angezeigt. Andere Male waren es drei, je nachdem, was gerade getestet wurde. MovilePay überließ die Wahl dem Server, anstatt eine Regel fest in die Anwendung zu codieren. Infolgedessen kennt die Anwendung nur die Definition des Einstiegspunkts des Dienstes und wie jeder einzelne Stil wiedergegeben wird. Ihr Server teilt mit, welche Dienste in welcher Reihenfolge generiert werden sollen. Hier sind einige Widgets, die die MovilePay-Anwendung anzeigen kann.

Widgets that the MovilePay application

Widgets in MovilePay application

Widgets in MovilePay application

Widgets in MovilePay application

Um also einen hoch anpassungsfähigen Startbildschirm zu erstellen, mussten wir vom Backend eine Antwort mit einer Liste von Abschnitten erhalten, die jeweils eine Liste von Widgets enthielten. Das Folgende ist ein Beispiel für eine JSON-Antwort, die die MovilePay-Anwendung analysieren und einen Startbildschirm wie im folgenden Beispiel erstellen sollte.

Parsing and creating a home screen

 [
    {
        "Titel" : "Abschnitt 1" ,
        "Widgets" : [
            {
                "Bezeichner" : "COLLECTION_WIDGET" ,
                "Inhalt" : [
                    {
                        "Titel" : "Titel A" ,
                        „Bild“ : „A“ ,
                        "Farbe" : "Gelb"
                    } ,
                    {
                        "Titel" : "Titel B" ,
                        "Bild" : "B" ,
                        "Farbe" : "Blau"
                    } ,
                    {
                        "Titel" : "Titel C" ,
                        "Bild" : "C" ,
                        "Farbe" : "rot"
                    } ,
                    {
                        "Titel" : "Titel D" ,
                        „Bild“ : „D“ ,
                        "Farbe" : "lila"
                    } ,
                    {
                        "Titel" : "Titel E" ,
                        „Bild“ : „E“ ,
                        "Farbe" : "Grün"
                    }
                ]
            } ,
            {
                "Bezeichner" : "IMAGES_WIDGET" ,
                "Inhalt" : [
                    {
                        „Bild“ : „Bild“ ,
                        "Farbe" : "Grün"
                    } ,
                    {
                        „Bild“ : „Bild“ ,
                        "Farbe" : "Blau"
                    } ,
                    {
                        „Bild“ : „Bild“ ,
                        "Farbe" : "Orange"
                    }
                ]
            } ,
            {
                "Bezeichner" : "COLLECTION_WIDGET" ,
                "Inhalt" : [
                    {"title" : "Title E" , "image" : "E" , "color" : "green" } , { "title" : "Title F" , "image" : "F" , "color" : "lila " } , { "Titel" : "Titel G" , "Bild" : "G" , "Farbe" : "Rot" } , { "Titel" : "Titel H" , "Bild" : "H" , "Farbe " : "blue" } , { "title" : "Title H" , "image" : "H" , "color" : "yellow" } ] } ] } , { "title" : "Section 2" , "widgets " : [ { "Bezeichner" : "CELLS_WIDGET" , "Inhalt" : [ { "Titel" : "Zelle 1" , "Farbe" : "rot" } , { "Titel" : "Zelle 2" , "Farbe" : „lila“ } , { „Titel“ : „Zelle 3“ , „Farbe“ : „gelb“ } , { „Titel“ : „Zelle 4“ , „Farbe“ : „blau“ } , { „Titel“ : „Zelle 5" , "farbe" : "dunkelgrün" } ] } ] } ]

Wir werden auch einige Bildschirme durchgehen, die mit Backend-gesteuerter Entwicklung erstellt werden können.

Home screens in backend-driven development

Flexible Navigation

Die von MovilePay entwickelte Super App hat ihr angestrebtes Ziel, innovativ zu sein, erreicht. MovilePay hat viel aus Hypothesentests gelernt, aber eine Sache, in der sie besonders gut sind, ist die Zahlungsabwicklung oder der Zahlungsprozess für verschiedene Dienstleistungen und Produkte. Sie hatten eine Zahlungs-App, die Zahlungen für alle von ihnen erbrachten Dienstleistungen verarbeiten konnte. Die Möglichkeit, Zahlungstransaktionen zu verwalten, war ein wesentlicher Vorteil, da MovilePay die Preise durch die Verarbeitung mehrerer Transaktionen senken und wertvolle Einblicke in das Verbraucherverhalten gewinnen konnte.

MovilePay beschloss, ein Zahlungs-SDK auf der Grundlage des Google Pay-, Apple Pay- oder VisaCheckout-Modells zu entwickeln, das in jede andere Anwendung integriert werden könnte.

Da die Arbeit an einem SDK jedoch sehr wenig Fähigkeit zum Testen mit typischen Entwicklungsmustern voraussetzt, ist eine Automatisierung erforderlich.

Da MovilePay Zahlungen abwickelte, war die Transformation der Ströme von entscheidender Bedeutung. MovilePay konnte es sich nicht leisten, seine Nutzer in irgendeiner Phase des Conversion-Funnels zu verlieren. Daher war es notwendig, den gesamten Geschäftsprozess zu optimieren – von der Benutzerregistrierung über das Hinzufügen einer Karte bis hin zum Bezahlen. Hier hat sich Backend-Driven Development wieder als nützlich erwiesen und den Server zum "Navigator" der Anwendung gemacht.

Optimizing the business process with BDD

Keiner der MovilePay-Anwendungsbildschirme wusste, welcher Bildschirm als nächstes kam. Nachdem der vorherige Bildschirm seine Aufgaben erledigt hatte, war der Server dafür verantwortlich, zurückzugeben, welcher Bildschirm als nächster angezeigt werden sollte.

Routen waren Aktionen, die eine Anwendung über eine als Router bekannte Struktur erkennen und darauf reagieren konnte. Der Anmeldemechanismus umfasste zwei separate Aktionszweige: einen für den Seitentitel und einen für andere Elemente (z. B. eine Änderung des Benutzernamens oder ein neues Passwort), die im Aktionsbaum angetroffen wurden. Der Router verarbeitete sie, indem er sie an den Server schickte, der dann ihre Aktionen interpretierte und festlegte, welche Interpretationen auf dem nächsten Bildschirm erscheinen sollten.

Dieser bescheidene Strategiewechsel ermöglichte eine Rationalisierung. MovilePay hat viele verschiedene Möglichkeiten ausprobiert, um das Anmeldeformular anzuzeigen. Es konnte getestet werden, ob es besser ist, den Kassenbildschirm vor oder nach dem Hinzufügen der Karte anzuzeigen. Dies ist zum Beispiel einer der Gründe, warum MovilePay seine Conversion-Rate im Vergleich zu anderen Zahlungsoptionen um 30 % steigern konnte.

Ein weiteres Beispiel ist, wie MovilePay Backend-Driven Development nutzte, um seine Probleme zu lösen. Wir denken, dass Sie sich wundernlernen, wie man diesen Ansatz in der Praxis anwendet. Lassen Sie mich Ihnen zeigen, wie einfach es ist.

Es gibt viele alternative Methoden, um das gleiche Ziel zu erreichen. Wir zeigen Ihnen, wie MovilePay es für iOS gemacht hat. Falls gewünscht, kann dieses Konzept auf jede andere Front-End-Plattform angewendet werden.

Als MovilePay es implementierte, berücksichtigten sie Anforderungen wie das einfache Hinzufügen zusätzlicher Widgets, die Lesbarkeit des Codes und die Einzelverantwortung. Um die Dinge einfacher und nativer zu machen, hat sich MovilePay entschieden, die Codable API für die Serialisierung zu verwenden.

Widgets (iOS)

In Bezug auf die Flexibilität war MovilePay der Ansicht, dass die beste Lösung darin bestünde, Widgets zu verallgemeinern, die mit einem Protokoll geparst werden müssen. MovilePay legt auch eine Aufzählung fest, um zu bestimmen, welche Widget-Struktur die Daten analysieren soll.

 Protokoll-Widget : Dekodierbar { }

enum WidgetIdentifier : String , Dekodierbar {
    case banner = "BANNER"
    Fallsammlung = "SAMMLUNG"
    Fallliste = "LISTE"

    var -Metatyp : Widget . Geben Sie {
        selbst wechseln {
        Fall . Werbebanner :
            BannerWidget zurückgeben . selbst
        Fall . Sammlung :
            CollectionWidget zurückgeben . selbst
        Fall . Liste :
            ListenWidget zurückgeben . selbst
        }
    }
}

Sie nutzen die Codable-API über das Decodierbare-Protokoll, das vom Widget-Protokoll implementiert wird. Nachfolgend finden Sie ein Beispiel für die Definition einer Widget-Struktur.

 struct BannerWidget : Widget {
    privat lassen imageURLString : Zeichenfolge
}

struct CollectionWidget : Widget {

    struct Item : Dekodierbar {
        let imageURLString : Zeichenfolge
        let title : String
        let subtitle : String
    }

    let sectionTitle : String
    Liste lassen : [ Artikel ]
}

struct ListWidget : Widget {

    struct Item : Dekodierbar {
        let imageURLString : Zeichenfolge
        Lassen Sie Text : Zeichenfolge
    }

    let sectionTitle : String
    Liste lassen : [ Artikel ]
}

Schließlich wurde es als Typlöschung definiert, die jedes Widget mit der erforderlichen Initialisierungsmethode interpretiert, wenn die von Decodable angegebene benutzerdefinierte Initialisierung geändert werden muss.

 letzte Klasse AnyWidget : Dekodierbar {

    private Enum CodingKeys : CodingKey {
        Fallkennung
    }

    Widget lassen : Widget ?

    erforderlich init ( von decoder : Decoder ) wirft {
        mach {
            let container = try decoder . Container ( keyedBy : CodingKeys . self )
            let type = try container . decode ( WidgetIdentifier . self , forKey : . Bezeichner )
            selbst . Widget = Versuchstyp . Metatyp . init ( von : Decoder )
        } fangen {
            selbst . Widget = Null
        }
    }
}

Dieser Löschtyp wird verwendet, um die Kennung des Widgets und seine "Metatyp"-Eigenschaft zu entschlüsseln, um zu bestimmen, welche Widget-Struktur verwendet werden soll, um die restlichen Daten des geparsten Widgets zu parsen.

All dies führt dazu, dass die folgende Struktur eine Antwort parsen kann, die alle Informationen über die Widgets enthält. Es hat eine einzigartige Funktion: eine Reihe von Widget-Protokolltypen und kann jedes Widget mithilfe der oben definierten Typlöschung entschlüsseln.

 struct HomeResponse : Decodierbar {

    private Enum CodingKeys : CodingKey {
        Fall -Widgets
    }

    Lassen Sie Widgets : [ Widget ]

    init ( von decoder : Decoder ) wirft {
        let container = try decoder . Container ( keyedBy : CodingKeys . self )
        selbst . Widgets = Versuchscontainer . decode ( [ AnyWidget ] . self , forKey : . widgetsss="Token-Interpunktion">) . compactMap {$ 0. Widget } } init ( Widgets : [ Widget ] ) { self . Widgets = Widgets } }

MovilePay hätte andere Optionen wählen können, z. B. das Protokoll nicht zu verwenden und sich darauf zu verlassen, dass das Backend ein Array jedes unterstützten Widgets zum Parsen zurückgibt. Wir haben jedoch festgestellt, dass unsere Protokollwahl die beste Wahl für Wartung und Lesbarkeit war. Dieser Ansatz reichte aus, um jedes Mal, wenn ein neues Widget erstellt werden musste, eine neue Struktur zu erstellen und der Aufzählung Fälle hinzuzufügen. Bei einem anderen System in einer ähnlichen Situation müsste das Design von HomeResponse geändert werden.

Nachfolgend finden Sie eine mögliche JSON-API-Antwort, die dieses Modell analysiert.

 {
    "Widgets" : [
        {
            "Kennung" : "BANNER" ,
            "imageURLString" : "url_image_to_be_downloaded"
        } ,
        {
            "Bezeichner" : "SAMMLUNG" ,
            "sectionTitle" : "Abschnittstitel" ,
            "Liste" : [
                {
                    "imageURLString" : "url_image_to_be_downloaded" ,
                    "title" : "Titelelement 1" ,
                    "subtitle" : "Untertitelelement 1"
                } ,
                {
                    "imageURLString" : "url_image_to_be_downloaded" ,
                    "title" : "Titelelement 2" ,
                    "subtitle" : "Untertitelelement 2"
                } ,
                {
                    "imageURLString" : "url_image_to_be_downloaded" ,
                    "title" : "Titelelement 3" ,
                    "subtitle" : "Untertitelelement 3"
                }
            ]
        } ,
        {
            „Bezeichner“ : „LISTE“ ,
            "sectionTitle" : "Abschnittstitel" ,
            "Liste" : [
                {
                    "imageURLString" : "url_image_to_be_downloaded" ,
                    "text" : "Textelement 1"
                } ,
                {
                    "imageURLString" : "url_image_to_be_downloaded" ,
                    "text" : "Textelement 2"
                } ,
                {
                    "imageURLString" : "url_image_to_be_downloaded" ,
                    "text" : "Textelement 3"
                }
            ]
        } ,
        {
            "Kennung" : "BANNER" ,
            "imageURLString" : "url_image_to_be_downloaded"
        }
    ]
}

Dieser Ansatz ist sehr nah an der Entwicklung der Super App, die es MovilePay ermöglichte, verschiedenen Benutzern verschiedene Dienste zu präsentieren, viele Anzeigeoptionen zu testen, Hypothesen zu erarbeiten und festzulegen, welches Widget für welchen Dienst verwendet wird. Die Änderung der Bildschirmsortierung und Dienstgruppierung kam dem, was MovilePay zuvor getan hatte, sehr nahe.

Navigation (iOS)

Nach der Behebung des Widget-Problems versuchte MovilePay, die Navigation auf ähnliche Weise zu verbessern. Sie erstellten das Action-Protokoll, das mit dem Widget-Protokoll identisch war.

Eine Aktion ist ein strukturiertes Objekt, das in der JSON-Antwort einiger MovilePay-APIs zurückgegeben wird, mit einer ID und allen Parametern, die in der Szene angezeigt werden sollen, die es darstellt. Folglich ist das Aktionsprotokoll dafür verantwortlich, beim Zerlegen des strukturierten Objekts zu helfen.

 Protokoll Aktion : Dekodierbar {
    func scene ( ) - > UIViewController
}

enum ActionIdentifier : String , Dekodierbar {
    case home = "HOME"
    fall screenOne = "SCREEN_ONE"
    case screenTwo = "SCREEN_TWO"

    var -Metatyp : Aktion . Geben Sie {
        selbst wechseln {
        Fall . Zuhause :
            zurück HomeAktion . selbst
        Fall . screenOneOken-Operator">: Rückgabe von ScreenOneAction . self case . ScreenTwo : Rückgabe von ScreenTwoAction . self } } } view raw

Der einzige Unterschied zwischen dem Aktionsprotokoll und dem Widget-Protokoll besteht darin, dass wir eine Methode bereitstellen, die die entsprechende Szene für jede Aktion in der Aktionsdefinition zurückgibt. Sehen Sie sich als Beispiel an, wie diese Maßnahmen umgesetzt werden.

 struct HomeAction : Aktion {
    func scene ( ) - > UIViewController {
        Rückkehr HomeCoordinator . Szene ( )
    }
}

struct ScreenOneAction : Aktion {
    let title : String

    func scene ( ) - > UIViewController {
        Rückgabe ScreenOneCoordinator . Szene ( Titel : selbst . Titel )
    }
}

struct ScreenTwoAction : Aktion {
    let title : String
    let subtitle : String

    func scene ( ) - > UIViewController {
        return ScreenTwoCoordinator . Szene ( Titel : selbst . Titel , Untertitel : selbst . Untertitel )
    }
}

Das obige Beispiel zeigt, dass Actions nach ihrer Erstellung alle erforderlichen Eigenschaften enthalten müssen, um ihre Szene zu initialisieren und die Coordinators-Methode aufzurufen, um den UIViewController zu instanziieren. Koordinatoren sind Strukturen, die einen UIViewController bereitstellen. Hier ist etwas zu erwähnen: MovilePay verwendet ein Entwurfsmuster in Coordinators-Strukturen, das durch eine statische scene()-Methode dargestellt wird, die mit der Generierung einer UIViewController-Instanz für jede Stufe beauftragt ist.

 Abschlussklasse HeimatKoordinator : Koordinator {
    Statische Funktionsszene ( ) - > UIViewController {
        // Erstellen Sie den ViewController und alle Architekturkomponenten dieser Szene ...
        gibt createdViewController zurück
    }
}

Abschlussklasse ScreenOneCoordinator : Koordinator {
    Statische Funktionsszene ( ) - > UIViewController {
        // Erstellen Sie den ViewController und alle Architekturkomponenten dieser Szene ...
        gibt createdViewController zurück
    }
}

Abschlussklasse ScreenTwoCoordinator : Koordinator {
    Statische Funktionsszene ( ) - > UIViewController {
        // Erstellen Sie den ViewController und alle Architekturkomponenten dieser Szene ...
        gibt createdViewController zurück
    }
}

Es sollte auch beachtet werden, dass trotz des gewählten architektonischen Entwurfsmusters (MVC, MVP, MVVM-C, VIPER-C, VIP oder jede andere Architektur, die einen Koordinator verwendet, um Szenen zu erstellen und von einer Szene zur anderen zu wechseln), die Implementierung verwendet wird Aktions- und Backend-getriebene Entwicklung ist durchaus angebracht.

Für den Actions MovilePay-Kontext haben wir die gleiche Art der Löschung wie für Widgets verwendet, mit einer leichten Anpassung.

 letzte Klasse AnyAction : Decodierbar {

    private Enum CodingKeys : CodingKey {
        Fallkennung
    }

    Aktion lassen : Aktion ?

    erforderlich init ( von decoder : Decoder ) wirft {
        mach {
            let container = try decoder . Container ( keyedBy : CodingKeys . self )
            let type = try container . decode ( ActionIdentifier . self , forKey : . Bezeichner )
            selbst . Aktion = Versuchstyp . Metatyp . init ( von : Decoder )
        } fangen {
            selbst . Aktion = null
        }
    }
}

Ein relevanter Hinweis hier ist, dass MovilePay das Generics-Entwurfsmuster verwenden könnte, um das Duplizieren von Typenlöschungscode zu vermeiden. Sie entschieden sich jedoch dagegen. Das Folgende ist ein Beispiel für eine Struktur, die eine Aktion enthält.

 struct ResponseModelForActions : Dekodierbar {
    private Enum -CodingKeys"token operator">: CodingKey { case action , text } let action : Action ? let text : String init ( from decoder : Decoder ) throws { let container = try decoder . container ( keyedBy : CodingKeys . self ) selbst . text = Versuchscontainer . decode ( String . self , forKey : . text ) let anyAction = try ? Behälter . decode ( AnyAction . self , forKey : . action ) self . action = anyAction ?. Aktion } }

Es kann JSON sein, das beispielsweise von einer API bereitgestellt wird, um einen UIButton auf dem Bildschirm zu erstellen. Das Aktionsobjekt, das verarbeitet wird, nachdem der Benutzer auf diese Schaltfläche tippt, kann die bereitgestellte Aktion ausführen, wodurch die Anwendung veranlasst wird, den Startbildschirm anzuzeigen.

 {
    "text" : "Demonstrativer Text" ,
    "Aktion" : {
        "Bezeichner" : "HOME_SCREEN"
    }
}

Dies wurde leicht erreicht, indem das Coordinator-Protokoll erweitert wurde, um allen Koordinatoren zu ermöglichen, eine neue Szene über das Actions-Objekt zu erhalten.

Es ermöglicht dem Koordinator zu handeln, d. h. die nächste UIViewController-Instanz für diese Aktion zu generieren und sie dann anzuzeigen.

 Erweiterungskoordinator {
    func scene ( mit action : Action ) - > UIViewController {
        Aktion zurück . Szene ( )
    }
}

Ein Hinweis zur Serverimplementierung

Sie fragen sich wahrscheinlich, wie das alles serverseitig aussieht. Wie können Sie Ihre Dienste und Kernkompetenzen nicht mit externen Informationen verwechseln? Das Geheimnis des Erfolgs in der Softwareentwicklung ist das Arbeiten in Schichten.

Daher fügte MovilePay zusätzlich zu all den komplexen Kerndiensten dem Server eine weitere Schicht hinzu, die durch Abstrahieren aller anderen Dienste und Anwenden der gesamten Anwendungslogik die Daten in die vom Frontend erwartete Antwort umwandelt. Diese Schicht heißt BFF oder Backend For Frontend ist eine Schicht, die die Kommunikation zwischen zwei getrennten und unabhängigen Enden des Systems bereitstellt. Hier werden Zeichenfolgen, Bilder, Streams und Stilvariationen konfiguriert und auf die zugrunde liegenden Daten angewendet, bevor sie an Anwendungen gesendet werden.

Schlussfolgerungen

Die Verwendung des Backend-gesteuerten Ansatzes hat mehrere Vorteile, die wir im gesamten Artikel zu verdeutlichen versucht haben. Dies ist jedoch nur eine weitere Lösungsvorlage. Es ist keine Wunderpille für die App-Entwicklung. Darüber hinaus muss der Kontext berücksichtigt werden, in dem die Anwendung verwendet werden soll. Müssen Sie Anwendungen für mehrere Plattformen erstellen? Welche Arten von Tests möchten Sie durchführen? Benötigen Sie die vollständige Kontrolle über alle Bildschirme? Wie groß werden Ihre Nutzlasten sein? Verfügen Sie über genügend Ressourcen, um dieses Projekt durchzuführen, einschließlich eines Entwicklungsteams, das in der Lage ist, diese Aufgaben zu bewältigen?

Vor allem sollten Sie immer darauf achten, welches Maß an Flexibilität Sie benötigen. Das Erstellen extrem vielseitiger UI-Komponenten oder die Verwendung von nicht standardmäßigen Schriftarten und Rändern kann die Codebasis unglaublich komplex machen, was zu einer schlechteren Benutzererfahrung führt.

Die meisten Projekte benötigen diese Art von Flexibilität nicht. Bei der Wahl eines Backend-getriebenen Ansatzes ist es wichtig zu überlegen, ob Sie die Ressourcen in Form von Finanzen und einem Entwicklungsteam haben, um ein solches Projekt zu entwickeln und zu pflegen, und ob Sie die Nutzlast Ihrer Anwendung richtig berechnen können.