Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. iOS SDK

Der richtige Weg, um den Status zwischen Swift View-Controllern zu teilen

by
Read Time:13 minsLanguages:

German (Deutsch) translation by Federicco Ancie (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Vor einigen Jahren, als ich noch Mitarbeiter einer mobilen Beratungsfirma war, arbeitete ich an einer App für eine große Investmentbank. Große Unternehmen, insbesondere Banken, verfügen normalerweise über die Prozesse, um sicherzustellen, dass ihre Software sicher, robust und wartbar ist.

Ein Teil dieses Prozesses bestand darin, den Code der App, die ich geschrieben habe, zur Überprüfung an einen Dritten zu senden. Das hat mich nicht gestört, weil ich dachte, mein Code sei in Ordnung und die Überprüfungsfirma würde dasselbe sagen.

Als ihre Antwort hatte, war das Urteil anders, als ich dachte. Während sie angaben, dass die Qualität des Codes gut war, stellten sie fest, dass der Code schwer zu warten und zu testen war (Unit-Tests waren zu dieser Zeit in der iOS-Entwicklung nicht sehr beliebt).

Ich wies ihr Urteil zurück und dachte, mein Code sei großartig und könne auf keinen Fall verbessert werden. Sie dürfen es einfach nicht verstehen!

Ich hatte die typische Entwickler-Hybris: Wir denken oft, dass das, was wir tun, großartig ist und andere es nicht verstehen.

Im Nachhinein habe ich mich geirrt. Wenig später begann ich über einige Best Practices zu lesen. Von da an stachen die Probleme in meinem Code wie ein schmerzender Daumen heraus. Ich stellte fest, dass ich, wie viele iOS-Entwickler, einigen klassischen Fallstricken schlechter Codierungspraktiken erlegen war.

Was die meisten iOS-Entwickler falsch machen

Eine der häufigsten schlechten Vorgehensweisen bei der iOS-Entwicklung tritt auf, wenn der Status zwischen den View-Controllern einer App übergeben wird. Ich selbst bin in der Vergangenheit in diese Falle geraten.

Die Statusweitergabe über View Controller ist in jeder iOS-App von entscheidender Bedeutung. Wenn Ihre Benutzer durch die Bildschirme Ihrer App navigieren und mit ihr interagieren, müssen Sie einen globalen Status beibehalten, der alle Änderungen protokolliert, die der Benutzer an den Daten vornimmt.

Und hier greifen die meisten iOS-Entwickler nach der offensichtlichen, aber falschen Lösung: dem Singleton-Muster.

Das Singleton-Muster ist sehr schnell zu implementieren, insbesondere in Swift, und es funktioniert gut. Sie müssen einer Klasse nur eine statische Variable hinzufügen, um eine gemeinsam genutzte Instanz der Klasse selbst zu behalten, und fertig.

Es ist dann einfach, von überall in Ihrem Code auf diese gemeinsam genutzte Instanz zuzugreifen:

Aus diesem Grund glauben viele Entwickler, die beste Lösung für das Problem der staatlichen Ausbreitung gefunden zu haben. Aber sie sind falsch.

Das Singleton-Muster wird tatsächlich als Anti-Muster betrachtet. In der Entwicklergemeinschaft gab es viele Diskussionen darüber. Siehe beispielsweise diese Frage zum Stack Overflow.

Kurz gesagt, Singletons verursachen folgende Probleme:

  • Sie führen viele Abhängigkeiten in Ihre Klassen ein, was es schwieriger macht, sie in Zukunft zu ändern.
  • Sie machen den globalen Status für jeden Teil Ihres Codes zugänglich. Dies kann zu komplexen Interaktionen führen, die schwer zu verfolgen sind und viele unerwartete Fehler verursachen.
  • Sie machen es sehr schwierig, Ihre Klassen zu testen, da Sie sie nicht einfach von einem Singleton trennen können.

An diesem Punkt denken einige Entwickler: "Ah, ich habe eine bessere Lösung. Ich werde stattdessen das AppDelegate verwenden."

Das Problem besteht darin, dass auf die AppDelegate-Klasse in iOS-Apps über die gemeinsam genutzte UIApplication-Instanz zugegriffen wird:

Die gemeinsam genutzte Instanz von UIApplication ist jedoch selbst ein Singleton. Sie haben also nichts gelöst!

Die Lösung für dieses Problem ist die Abhängigkeitsinjektion. Abhängigkeitsinjektion bedeutet, dass eine Klasse keine eigenen Abhängigkeiten abruft oder erstellt, sondern diese von außen empfängt.

Um zu sehen, wie die Abhängigkeitsinjektion in iOS-Apps verwendet wird und wie sie die Statusfreigabe ermöglicht, müssen wir zunächst eines der grundlegenden Architekturmuster von iOS-Apps erneut betrachten: das Model-View-Controller-Muster.

Erweitern des MVC-Musters

Kurz gesagt, das MVC-Muster besagt, dass die Architektur einer iOS-App drei Ebenen umfasst:

  • Die Modellebene repräsentiert die Daten einer App.
  • Die Ansichtsebene zeigt Informationen auf dem Bildschirm an und ermöglicht die Interaktion.
  • Die Controller-Schicht fungiert als Klebstoff zwischen den beiden anderen Schichten und verschiebt Daten zwischen ihnen.

Die übliche Darstellung des MVC-Musters sieht ungefähr so aus:

Simplistic view of the MVC patternSimplistic view of the MVC patternSimplistic view of the MVC pattern

Das Problem ist, dass dieses Diagramm falsch ist.

Dieses "Geheimnis" ist in der Apple-Dokumentation in einigen Zeilen deutlich zu erkennen:

"Man kann die von einem Objekt gespielten MVC-Rollen zusammenführen, sodass ein Objekt beispielsweise sowohl die Controller- als auch die Ansichtsrolle erfüllt. In diesem Fall wird es als Ansichtscontroller bezeichnet. Auf die gleiche Weise können Sie auch Modell-Controller-Objekte haben."

Viele Entwickler glauben, dass View Controller die einzigen Controller sind, die in einer iOS-App vorhanden sind. Aus diesem Grund wird viel Code in sie geschrieben, weil es keinen besseren Ort gibt. Dies bringt Entwickler dazu, Singletons zu verwenden, wenn sie den Status verbreiten müssen: Es scheint die einzig mögliche Lösung zu sein.

Aus den oben zitierten Zeilen geht hervor, dass wir unserem Verständnis des MVC-Musters eine neue Entität hinzufügen können: den Modellcontroller. Modellcontroller befassen sich mit dem Modell der App und erfüllen die Rollen, die das Modell selbst nicht erfüllen sollte. So sollte das obige Schema aussehen:

Diagram of the MVC pattern updated with view and model controllersDiagram of the MVC pattern updated with view and model controllersDiagram of the MVC pattern updated with view and model controllers

Das perfekte Beispiel dafür, wann ein Modellcontroller nützlich ist, ist das Beibehalten des App-Status. Das Modell sollte nur die Daten Ihrer App darstellen. Der Status der App sollte nicht von Belang sein.

Diese Zustandserhaltung endet normalerweise in View-Controllern, aber jetzt haben wir einen neuen und besseren Ort, um es auszudrücken: einen Modell-Controller. Dieser Modellcontroller kann dann übergeben werden, um Controller anzuzeigen, wenn sie durch Abhängigkeitsinjektion auf dem Bildschirm angezeigt werden.

Wir haben das Singleton-Anti-Pattern gelöst. Sehen wir uns unsere Lösung in der Praxis anhand eines Beispiels an.

Weitergabe des Status über View Controller mithilfe der Abhängigkeitsinjektion

Wir werden eine einfache App schreiben, um ein konkretes Beispiel dafür zu sehen, wie dies funktioniert. Die App zeigt Ihr Lieblingszitat auf einem Bildschirm an und ermöglicht es Ihnen, das Zitat auf einem zweiten Bildschirm zu bearbeiten.

Dies bedeutet, dass unsere App zwei Ansichts-Controller benötigt, die den Status gemeinsam nutzen müssen. Nachdem Sie gesehen haben, wie diese Lösung funktioniert, können Sie das Konzept auf Apps jeder Größe und Komplexität erweitern.

Zu Beginn benötigen wir einen Modelltyp zur Darstellung der Daten, in unserem Fall ein Zitat. Dies kann mit einer einfachen Struktur erfolgen:

Der Model Controller

Wir müssen dann einen Modellcontroller erstellen, der den Status der App enthält. Dieser Modellcontroller muss eine Klasse sein. Dies liegt daran, dass wir eine einzelne Instanz benötigen, die wir an alle unsere View-Controller übergeben. Werttypen wie Strukturen werden kopiert, wenn wir sie weitergeben, sodass sie eindeutig nicht die richtige Lösung sind.

In unserem Beispiel benötigt unser Modellcontroller lediglich eine Eigenschaft, in der das aktuelle Angebot beibehalten werden kann. In größeren Apps können Modellcontroller natürlich komplexer sein:

Ich habe der Quoteigenschaft einen Standardwert zugewiesen, damit beim Start der App bereits etwas auf dem Bildschirm angezeigt wird. Dies ist nicht erforderlich, und Sie können die Eigenschaft als optional auf nil initialisiert deklarieren, wenn Sie möchten, dass Ihre App mit einem leeren Status gestartet wird.

Erstellen Sie die Benutzeroberfläche

Wir haben jetzt den Modellcontroller, der den Status unserer App enthält. Als nächstes benötigen wir die View Controller, die die Bildschirme unserer App darstellen.

Zuerst erstellen wir ihre Benutzeroberflächen. So sehen die beiden View Controller im Storyboard der App aus.

view controllers in the storyboardview controllers in the storyboardview controllers in the storyboard

Die Benutzeroberfläche des First View Controllers besteht aus mehreren Beschriftungen und einer Schaltfläche, die mit einfachen Einschränkungen für das automatische Layout zusammengestellt sind. (Weitere Informationen zum automatischen Layout finden Sie hier auf Envato Tuts+.)

Die Benutzeroberfläche des zweiten Ansichtscontrollers ist dieselbe, verfügt jedoch über eine Textansicht zum Bearbeiten des Textes des Zitats und ein Textfeld zum Bearbeiten des Autors.

Die beiden Ansichtssteuerungen sind durch einen einzigen modalen Präsentationsabschnitt verbunden, der über die Schaltfläche Zitat bearbeiten erstellt wird.

Sie können die Benutzeroberfläche und die Einschränkungen der View-Controller im GitHub-Repo erkunden.

Codieren Sie einen View Controller mit Abhängigkeitsinjektion

Wir müssen jetzt unsere View Controller codieren. Das Wichtige, was wir hier beachten müssen, ist, dass sie die Modellcontrollerinstanz von außen durch Abhängigkeitsinjektion empfangen müssen. Daher müssen sie zu diesem Zweck eine Eigenschaft freigeben.

Wir können unseren ersten View Controller QuoteViewController aufrufen. Dieser Ansichts-Controller benötigt einige Steckdosen zu den Etiketten für das Zitat und den Autor in seiner Benutzeroberfläche.

Wenn dieser Ansichts-Controller auf dem Bildschirm angezeigt wird, füllen wir seine Benutzeroberfläche aus, um das aktuelle Angebot anzuzeigen. Wir setzen den Code dazu in die viewWillAppear(_:)-Methode des Controllers.

Wir hätten diesen Code stattdessen in die viewDidLoad()-Methode einfügen können, was durchaus üblich ist. Das Problem ist jedoch, dass viewDidLoad() nur einmal aufgerufen wird, wenn der View Controller erstellt wird. In unserer App müssen wir die Benutzeroberfläche von QuoteViewController jedes Mal aktualisieren, wenn sie auf dem Bildschirm angezeigt wird. Dies liegt daran, dass der Benutzer das Angebot auf dem zweiten Bildschirm bearbeiten kann.

Aus diesem Grund verwenden wir anstelle von viewDidLoad() die viewWillAppear(_:)-Methode. Auf diese Weise können wir die Benutzeroberfläche des View Controllers jedes Mal aktualisieren, wenn sie auf dem Bildschirm angezeigt wird. Wenn Sie mehr über den Lebenszyklus eines View Controllers und alle Methoden erfahren möchten, die aufgerufen werden, habe ich einen Artikel geschrieben, in dem alle beschrieben werden.

Der Edit View Controller

Wir müssen jetzt den zweiten Ansichts-Controller codieren. Wir werden diesen einen EditViewController nennen.

Dieser Ansichts-Controller ist wie der vorherige:

  • Es verfügt über Ausgänge für die Textansicht und das Textfeld, mit dem der Benutzer das Angebot bearbeitet.
  • Es hat eine Eigenschaft für die Abhängigkeitsinjektion der Modellcontrollerinstanz.
  • Es füllt seine Benutzeroberfläche, bevor es auf den Bildschirm kommt.

In diesem Fall habe ich die viewDidLoad()-Methode verwendet, da dieser Ansichtscontroller nur einmal auf dem Bildschirm angezeigt wird.

Den Staat teilen

Wir müssen jetzt den Status zwischen den beiden Ansichts-Controllern übergeben und ihn aktualisieren, wenn der Benutzer das Angebot bearbeitet.

Wir übergeben den App-Status in der prepare(for:sender:)-Methode von QuoteViewController. Diese Methode wird durch den verbundenen Übergang ausgelöst, wenn der Benutzer auf die Schaltfläche Zitat bearbeiten tippt.

Hier geben wir die Instanz des ModelControllers weiter, die den Status der App beibehält. Hier erfolgt die Abhängigkeitsinjektion für den EditViewController.

Im EditViewController müssen wir den Status auf das neu eingegebene Angebot aktualisieren, bevor wir zum vorherigen View Controller zurückkehren. Wir können dies in einer Aktion tun, die mit der Schaltfläche Speichern verbunden ist:

Initialisieren Sie den Model Controller

Wir sind fast fertig, aber Sie haben vielleicht bemerkt, dass uns noch etwas fehlt: Der QuoteViewController übergibt den ModelController durch Abhängigkeitsinjektion an den EditViewController. Aber wer gibt diese Instanz überhaupt an den QuoteViewController weiter? Denken Sie daran, dass ein Ansichtscontroller bei Verwendung der Abhängigkeitsinjektion keine eigenen Abhängigkeiten erstellen sollte. Diese müssen von außen kommen.

Vor dem QuoteViewController gibt es jedoch keinen View Controller, da dies der erste View Controller unserer App ist. Wir benötigen ein anderes Objekt, um die ModelController-Instanz zu erstellen und an den QuoteViewController zu übergeben.

Dieses Objekt ist das AppDelegate. Die Rolle des App-Delegaten besteht darin, auf die Lebenszyklusmethoden der App zu reagieren und die App entsprechend zu konfigurieren. Eine dieser Methoden ist die application(_:didFinishLaunchingWithOptions:, die beim Start der App aufgerufen wird. Hier erstellen wir die Instanz des ModelControllers und übergeben sie an den QuoteViewController:

Unsere App ist jetzt fertig. Jeder View Controller erhält Zugriff auf den globalen Status der App, wir verwenden jedoch nirgendwo in unserem Code Singletons.

Sie können das Xcode-Projekt für diese Beispiel-App im Tutorial GitHub repo herunterladen.

Abschluss

In diesem Artikel haben Sie gesehen, wie die Verwendung von Singletons zur Verbreitung des Status in einer iOS-App eine schlechte Praxis ist. Singletons verursachen viele Probleme, obwohl sie sehr einfach zu erstellen und zu verwenden sind.

Wir haben das Problem gelöst, indem wir uns das MVC-Muster genauer angesehen und die darin verborgenen Möglichkeiten verstanden haben. Durch die Verwendung von Modellcontrollern und die Abhängigkeitsinjektion konnten wir den Status der App auf alle Ansichtscontroller übertragen, ohne Singletons zu verwenden.

Dies ist eine einfache Beispiel-App, aber das Konzept kann auf Apps beliebiger Komplexität verallgemeinert werden. Dies ist die bewährte Standardmethode zur Weitergabe des Status in iOS-Apps. Ich benutze es jetzt in jeder App, die ich für meine Kunden schreibe.

Einige Dinge, die Sie beachten sollten, wenn Sie das Konzept auf größere Apps erweitern:

  • Der Modellcontroller kann den Status der App beispielsweise in einer Datei speichern. Auf diese Weise werden unsere Daten jedes Mal gespeichert, wenn wir die App schließen. Sie können auch eine komplexere Speicherlösung verwenden, z. B. Core Data. Ich empfehle, diese Funktionalität in einem separaten Modellcontroller zu speichern, der sich nur um die Speicherung kümmert. Dieser Controller kann dann von dem Modell-Controller verwendet werden, der den Status der App beibehält.
  • In einer App mit einem komplexeren Ablauf befinden sich viele Container in Ihrem App-Ablauf. Dies sind normalerweise Navigationscontroller mit gelegentlichem Tab-Bar-Controller. Das Konzept der Abhängigkeitsinjektion gilt weiterhin, Sie müssen jedoch die Container berücksichtigen. Sie können entweder beim Ausführen der Abhängigkeitsinjektion in die enthaltenen Ansichtscontroller eintauchen oder benutzerdefinierte Containerunterklassen erstellen, die den Modellcontroller weitergeben.
  • Wenn Sie Ihrer App ein Netzwerk hinzufügen, sollte dies auch in einem separaten Modellcontroller erfolgen. Ein Ansichtscontroller kann eine Netzwerkanforderung über diesen Netzwerkcontroller ausführen und dann die resultierenden Daten an den Modellcontroller übergeben, der den Status beibehält. Denken Sie daran, dass die Rolle eines Ansichtscontrollers genau die folgende ist: als Klebeobjekt zu fungieren, das Daten zwischen Objekten weitergibt.

Weitere Tipps und Best Practices für die Entwicklung von iOS-Apps finden Sie hier!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.