Ich möchte ein EnvironmentObject erstellen, auf das das Ansichtsmodell zugreifen kann (nicht nur die Ansicht).
Das Umgebungsobjekt verfolgt die Anwendungssitzungsdaten, z. B. angemeldet, Zugriffstoken usw. Diese Daten werden an die Ansichtsmodelle (oder bei Bedarf an Serviceklassen) übergeben, damit eine API aufgerufen werden kann, um Daten von diesen EnvironmentObjects zu übergeben.
Ich habe versucht, das Sitzungsobjekt aus der Ansicht an den Initialisierer der Ansichtsmodellklasse zu übergeben, erhalte jedoch eine Fehlermeldung.
Wie kann ich mit SwiftUI auf das EnvironmentObject zugreifen / es an das Ansichtsmodell übergeben?
Siehe Link zum Testprojekt: https://gofile.io/?c=vgHLVx
Antworten:
Ich habe kein ViewModel. (Vielleicht Zeit für ein neues Muster?)
Ich habe mein Projekt mit einer
RootView
und einigen untergeordneten Ansichten eingerichtet. Ich richte mein ObjektRootView
mit einemApp
Objekt als EnvironmentObject ein. Anstelle des ViewModel, das auf Modelle zugreift, greifen alle meine Ansichten auf Klassen in der App zu. Anstelle des ViewModel, das das Layout bestimmt, bestimmt die Ansichtshierarchie das Layout. Nachdem ich dies in der Praxis für einige Apps getan habe, habe ich festgestellt, dass meine Ansichten klein und spezifisch bleiben. Zur Vereinfachung:In meinen Voransichten initialisiere ich
MockApp
eine Unterklasse vonApp
. Die MockApp initialisiert die festgelegten Initialisierer mit dem verspotteten Objekt. Hier muss der UserService nicht verspottet werden, die Datenquelle (dh NetworkManagerProtocol) jedoch.quelle
app.userService.logout()
.userService
sollte privat sein und nur innerhalb der App-Klasse aufgerufen werden. Der obige Code sollte folgendermaßen aussehen:Button(action: { app.logout() })
Die Abmeldefunktion wird dann direkt aufgerufenuserService.logout()
.Das solltest du nicht. Es ist ein weit verbreitetes Missverständnis, dass SwiftUI am besten mit MVVM funktioniert.
MVVM hat keinen Platz in SwfitUI. Sie fragen, ob Sie ein Rechteck auf schieben können
eine Dreiecksform anpassen. Es würde nicht passen.
Beginnen wir mit einigen Fakten und arbeiten Schritt für Schritt:
ViewModel ist ein Modell in MVVM.
MVVM berücksichtigt keinen Werttyp (z. B. in Java nicht).
Ein Werttypmodell (Modell ohne Status) gilt als sicherer als Referenz
Typmodell (Modell mit Zustand) im Sinne der Unveränderlichkeit.
In MVVM müssen Sie jetzt ein Modell so einrichten, dass es bei jeder Änderung geändert wird
aktualisiert die Ansicht auf eine vorher festgelegte Weise. Dies wird als Bindung bezeichnet.
Ohne Bindung haben Sie keine schöne Trennung von Bedenken, z. Refactoring aus
Modell und zugehörige Zustände und deren Trennung von der Ansicht.
Dies sind die beiden Dinge, die die meisten iOS MVVM-Entwickler versagen:
iOS hat keinen "Bindungs" -Mechanismus im traditionellen Java-Sinne.
Einige würden die Bindung einfach ignorieren und denken, ein Objekt ViewModel aufzurufen
löst automatisch alles; Einige würden KVO-basierten Rx einführen, und
komplizieren Sie alles, wenn MVVM die Dinge einfacher machen soll.
Modell mit Staat ist einfach zu gefährlich
weil MVVM zu viel Wert auf ViewModel legt, zu wenig auf die Statusverwaltung
und allgemeine Disziplinen bei der Verwaltung der Kontrolle; Die meisten Entwickler landen am Ende
Denken, ein Modell mit Status, der zum Aktualisieren der Ansicht verwendet wird, ist wiederverwendbar und
testbar .
Aus diesem Grund führt Swift in erster Linie den Werttyp ein. ein Modell ohne
Zustand.
Nun zu Ihrer Frage: Sie fragen, ob Ihr ViewModel Zugriff auf EnvironmentObject (EO) haben kann?
Das solltest du nicht. Denn in SwiftUI hat ein Modell, das der Ansicht entspricht, automatisch
Verweis auf EO. Z.B;
Ich hoffe, die Leute können verstehen, wie kompakt das SDK ist.
In SwiftUI erfolgt MVVM automatisch . Es ist kein separates ViewModel-Objekt erforderlich
Das wird manuell an die Ansicht gebunden, für die eine EO-Referenz erforderlich ist.
Der obige Code ist MVVM. Z.B; ein Modell mit Bindung zur Ansicht.
Aber weil Modell ein Werttyp ist, anstatt Modell und Status als umzugestalten
Wenn Sie das Modell anzeigen, überarbeiten Sie die Kontrolle (z. B. in der Protokollerweiterung).
Dies ist das offizielle SDK, das das Designmuster an die Sprachfunktion anpasst und nicht nur
Durchsetzung. Substanz über Form.
Schauen Sie sich Ihre Lösung an, Sie müssen Singleton verwenden, das im Grunde global ist. Du
sollte wissen, wie gefährlich es ist, global überall ohne Schutz von zuzugreifen
Unveränderlichkeit, die Sie nicht haben, weil Sie Referenztyp-Modell verwenden müssen!
TL; DR
In SwiftUI wird MVVM nicht auf Java-Weise ausgeführt. Und der schnelle Weg, dies zu tun, ist nicht nötig
dafür ist es bereits eingebaut.
Hoffe, dass mehr Entwickler dies sehen, da dies eine beliebte Frage zu sein schien.
quelle
Unten finden Sie einen Ansatz, der für mich funktioniert. Getestet mit vielen Lösungen, die mit Xcode 11.1 gestartet wurden.
Das Problem ist auf die Art und Weise zurückzuführen, wie EnvironmentObject im allgemeinen Schema der Ansicht injiziert wird
dh in der ersten erstellten Ansicht, im zweiten erstellten Umgebungsobjekt, im dritten in die Ansicht eingefügten Umgebungsobjekt
Wenn ich also ein Ansichtsmodell im Ansichtskonstruktor erstellen / einrichten muss, ist das Umgebungsobjekt dort noch nicht vorhanden.
Lösung: Brechen Sie alles auseinander und verwenden Sie die explizite Abhängigkeitsinjektion
So sieht es im Code aus (generisches Schema)
Hier gibt es keinen Kompromiss, da ViewModel und EnvironmentObject von Natur aus Referenztypen sind (eigentlich
ObservableObject
), daher übergebe ich hier und da nur Referenzen (auch Zeiger genannt).quelle