Eine Abhängigkeitsmanagementbibliothek, inspiriert von Swiftuis "Umgebung".
Diese Bibliothek war im Laufe vieler Folgen auf Point-Free motiviert und gestaltet, eine Videoreihe, in der die funktionale Programmierung und die Swift-Sprache erforscht werden, die von Brandon Williams und Stephen Celis gehostet wird.
Abhängigkeiten sind die Typen und Funktionen in Ihrer Anwendung, die mit externen Systemen interagieren müssen, die Sie nicht kontrollieren. Klassische Beispiele hierfür sind API -Clients, die Netzwerkanfragen für Server stellen, aber auch scheinbar harmlose Dinge wie UUID
und Date
-Initialisierer, Dateizugriff, Benutzerstandards und sogar Uhren und Timer können als Abhängigkeiten angesehen werden.
Sie können wirklich weit in der Anwendungsentwicklung kommen, ohne jemals über das Abhängigkeitsmanagement nachzudenken (oder, wie manche es gerne nennen, "Abhängigkeitsinjektion"), aber letztendlich unkontrollierte Abhängigkeiten können viele Probleme in Ihrem Codebasis und in Ihrem Entwicklungszyklus verursachen:
Unkontrollierte Abhängigkeiten erschweren es schwierig, schnelle, deterministische Tests zu schreiben , da Sie anfällig für die Unstimmigkeiten der äußeren Welt sind, z. B. Dateisysteme, Netzwerkkonnektivität, Internetgeschwindigkeit, Server -Verfügbarkeit und vieles mehr.
Viele Abhängigkeiten funktionieren in Swiftui -Voransichten nicht gut , wie Standortmanager und Spracherkenner, und einige arbeiten nicht einmal in Simulatoren wie Bewegungsmanagern und vielem mehr. Dies verhindert, dass Sie das Design von Funktionen leicht wiederholen können, wenn Sie diese Frameworks verwenden.
Abhängigkeiten, die mit Drittanbietern, Nicht-Appl-Bibliotheken (wie Firebase, Web Socket-Bibliotheken, Netzwerkbibliotheken usw.) interagieren, sind in der Regel im Schwergewicht und es dauert lange, bis sie kompiliert werden . Dies kann Ihren Entwicklungszyklus verlangsamen.
Aus diesen Gründen und vielem mehr wird es sehr ermutigt, die Kontrolle über Ihre Abhängigkeiten zu übernehmen, anstatt sie zu kontrollieren.
Die Kontrolle einer Abhängigkeit ist jedoch nur der Anfang. Sobald Sie Ihre Abhängigkeiten kontrolliert haben, stehen Sie mit einer Reihe neuer Probleme konfrontiert:
Wie können Sie die Abhängigkeiten während Ihrer gesamten Anwendung auf eine Weise verbreiten , die ergonomischer ist, als sie explizit überall herumzugeben, aber sicherer als eine globale Abhängigkeit zu haben?
Wie können Sie Abhängigkeiten für nur einen Teil Ihrer Anwendung überschreiben ? Dies kann für überschreibende Abhängigkeiten für Tests und Swiftui -Vorschau sowie bestimmte Benutzerströme wie Onboarding -Erlebnisse nützlich sein.
Wie können Sie sicher sein, dass Sie alle Abhängigkeiten überschreiben, die eine Funktion in Tests verwendet? Es wäre falsch, dass ein Test einige Abhängigkeiten ausgibt, aber andere als interagieren mit der Außenwelt lassen.
Diese Bibliothek befasst sich mit allen oben genannten Punkten und vielem mehr.
Mit der Bibliothek können Sie Ihre eigenen Abhängigkeiten registrieren, aber auch viele kontrollierbare Abhängigkeiten aus dem Box (siehe DependencyValues
für eine vollständige Liste), und es besteht eine gute Chance, dass Sie sofort eine verwenden können. Wenn Sie Date()
, UUID()
, Task.sleep
oder kombinieren Sie Scheduler direkt in der Logik Ihrer Funktion, können Sie diese Bibliothek bereits verwenden.
@ Observable
final class FeatureModel {
var items : [ Item ] = [ ]
@ ObservationIgnored
@ Dependency ( . continuousClock ) var clock // Controllable way to sleep a task
@ ObservationIgnored
@ Dependency ( . date . now ) var now // Controllable way to ask for current date
@ ObservationIgnored
@ Dependency ( . mainQueue ) var mainQueue // Controllable scheduling on main queue
@ ObservationIgnored
@ Dependency ( . uuid ) var uuid // Controllable UUID creation
// ...
}
Sobald Ihre Abhängigkeiten deklariert sind, anstatt sich an das Date()
, UUID()
usw. direkt zu wenden, können Sie die Abhängigkeit, die auf dem Modell Ihrer Funktion definiert ist, verwenden:
@ Observable
final class FeatureModel {
// ...
func addButtonTapped ( ) async throws {
try await clock . sleep ( for : . seconds ( 1 ) ) // ? Don't use 'Task.sleep'
items . append (
Item (
id : uuid ( ) , // ? Don't use 'UUID()'
name : " " ,
createdAt : now // ? Don't use 'Date()'
)
)
}
}
Dies ist alles, was es braucht, um kontrollierbare Abhängigkeiten in Ihren Funktionen zu verwenden. Mit dieser kleinen Vorabarbeit können Sie die Kräfte der Bibliothek ausnutzen.
Beispielsweise können Sie diese Abhängigkeiten in Tests problemlos steuern. Wenn Sie die Logik in der Methode addButtonTapped
testen möchten, können Sie die Funktion withDependencies
verwenden, um Abhängigkeiten für den Umfang eines einzelnen Tests zu überschreiben. Es ist so einfach wie 1-2-3:
@ Test
func add ( ) async throws {
let model = withDependencies {
// 1️⃣ Override any dependencies that your feature uses.
$0 . clock = . immediate
$0 . date . now = Date ( timeIntervalSinceReferenceDate : 1234567890 )
$0 . uuid = . incrementing
} operation : {
// 2️⃣ Construct the feature's model
FeatureModel ( )
}
// 3️⃣ The model now executes in a controlled environment of dependencies,
// and so we can make assertions against its behavior.
try await model . addButtonTapped ( )
#expect (
model . items == [
Item (
id : UUID ( uuidString : " 00000000-0000-0000-0000-000000000000 " ) ! ,
name : " " ,
createdAt : Date ( timeIntervalSinceReferenceDate : 1234567890 )
)
]
)
}
Hier haben wir die date
so gesteuert, dass ImmediateClock
immer das gleiche Datum zurückgeben, und die uuid
-Abhängigkeit so kontrolliert haben clock
dass sie jedes Mal, wenn sie aufgerufen wird Sofort. Wenn wir diese Abhängigkeiten nicht kontrollieren würden, wäre dieser Test sehr schwer zu schreiben, da es keine Möglichkeit gibt, genau vorherzusagen, was bis zum Date()
und UUID()
, und wir müssten auf die Zeit der realen Welt warten, um zu bestehen. den Test langsam machen.
Steuerbare Abhängigkeiten sind jedoch nicht nur für Tests nützlich. Sie können auch in Xcode -Voransichten verwendet werden. Angenommen, die obige Funktion nutzt eine Uhr, um eine Zeit zu schlafen, bevor etwas in der Ansicht passiert. Wenn Sie nicht buchstäblich auf die Zeit warten möchten, um zu sehen, wie sich die Ansicht ändert, können Sie die Taktabhängigkeit als "sofortige" Uhr unter Verwendung der .dependencies
-Vorschau -Eigenschaften überschreiben:
#Preview (
traits : . dependencies {
$0 . continuousClock = . immediate
}
) {
// All access of '@Dependency(.continuousClock)' in this preview will
// use an immediate clock.
FeatureView ( model : FeatureModel ( ) )
}
Dies macht es so, dass die Vorschau beim Ausführen eine sofortige Uhr verwendet. Wenn Sie jedoch in einem Simulator oder auf dem Gerät ausgeführt werden, verwendet sie weiterhin ein Live ContinuousClock
. Dies ermöglicht es, Abhängigkeiten nur für Voransichten zu überschreiben, ohne sich zu beeinflussen, wie Ihre App in der Produktion ausgeführt wird.
Das sind die Grundlagen für die Verwendung der Bibliothek, aber Sie können noch viel mehr tun. Sie können die Bibliothek ausführlicher erfahren, indem Sie die Dokumentation und Artikel untersuchen:
Schneller Start (wie die obigen Informationen) : Erfahren Sie die Grundlagen des Beginns der Bibliothek, bevor Sie tief in alle Funktionen eintauchen.
Was sind Abhängigkeiten? : Erfahren Sie, was Abhängigkeiten sind, wie sie Ihren Code komplizieren und warum Sie sie kontrollieren möchten.
Verwenden von Abhängigkeiten : Erfahren Sie, wie Sie die in der Bibliothek registrierten Abhängigkeiten verwenden.
Registrieren von Abhängigkeiten : Erfahren Sie, wie Sie Ihre eigenen Abhängigkeiten bei der Bibliothek registrieren, damit sie sofort von einem Teil Ihrer Codebasis verfügbar sind.
Live-, Vorschau- und Testabhängigkeiten : Erfahren Sie, wie Sie unterschiedliche Implementierungen Ihrer Abhängigkeiten zur Verwendung in der Live -Anwendung sowie in Xcode -Vorschau sowie in Tests bereitstellen.
Testen : Einer der Hauptgründe für die Kontrolle der Abhängigkeiten ist die Ermöglichung eines einfacheren Tests. Erfahren Sie einige Tipps und Tricks, um bessere Tests mit der Bibliothek zu schreiben.
Entwerfen von Abhängigkeiten : Lernen Sie Techniken zum Entwerfen Ihrer Abhängigkeiten, damit sie am flexibelsten für die Einspritzung in Funktionen und Überschreibungen für Tests sind.
Übergeordnete Abhängigkeiten : Erfahren Sie, wie Abhängigkeiten zur Laufzeit geändert werden können, damit bestimmte Teile Ihrer Anwendung unterschiedliche Abhängigkeiten verwenden können.
Abhängigkeitslebensdauer : Lernen Sie die Lebensdauer der Abhängigkeiten kennen, wie die Lebensdauer einer Abhängigkeit verlängert und wie Abhängigkeiten vererbt werden.
Einzelne Einstiegspunktsysteme : Erfahren Sie mehr über "einzelne Einstiegspunkte" -Systeme und warum sie für diese Abhängigkeitsbibliothek am besten geeignet sind, obwohl es möglich ist, die Bibliothek mit Nicht-Eingangs-Einstiegspunktsystemen zu verwenden.
Wir haben die Scrumdinger -Demo -Anwendung von Apple mit modernen Best Practices für die Entwicklung von Swiftui neu aufgebaut, einschließlich der Verwendung dieser Bibliothek, um Abhängigkeiten im Dateisystemzugriff, Timer und Spracherkennungs -APIs zu steuern. Diese Demo kann hier gefunden werden.
Die neueste Dokumentation für die Abhängigkeiten -APIs ist hier verfügbar.
Sie können einem Xcode -Projekt Abhängigkeiten hinzufügen, indem Sie es Ihrem Projekt als Paket hinzufügen.
https://github.com/pointfreeco/swift-pendencies
Wenn Sie Abhängigkeiten in einem SWIFTPM -Projekt verwenden möchten, ist es so einfach wie das Hinzufügen zu Ihrem Package.swift
.
dependencies: [
. package ( url : " https://github.com/pointfreeco/swift-dependencies " , from : " 1.0.0 " )
]
Und dann das Produkt zu einem beliebigen Ziel hinzufügen, das Zugriff auf die Bibliothek benötigt:
. product ( name : " Dependencies " , package : " swift-dependencies " ) ,
Wenn Sie diese Bibliothek besprechen oder eine Frage haben möchten, wie Sie sie verwenden können, um ein bestimmtes Problem zu lösen, gibt es eine Reihe von Orten, die Sie mit anderen punktfreien Enthusiasten besprechen können:
Diese Bibliothek steuert eine Reihe von Abhängigkeiten nicht in der Box, ist aber auch für Erweiterung geöffnet. Die folgenden Projekte bauen alle auf Abhängigkeiten auf:
Es gibt viele andere Abhängigkeitsinjektionsbibliotheken in der Swift -Community. Jeder hat seine eigenen Prioritäten und Kompromisse, die sich von Abhängigkeiten unterscheiden. Hier sind einige bekannte Beispiele:
Diese Bibliothek wird unter der MIT -Lizenz veröffentlicht. Einzelheiten siehe Lizenz.