
Nach der Teilnahme an der State of the Union-Sitzung auf der WWDC 2019 entschied ich mich, SwiftUI intensiv zu studieren. Ich habe viel Zeit damit verbracht, daran zu arbeiten, und nun habe ich mit der Entwicklung einer echten Anwendung begonnen, die fĂŒr eine breite Nutzergruppe nĂŒtzlich sein könnte.
Ich nannte sie MovieSwiftUI â eine App zur Suche nach neuen und alten Filmen sowie deren Sammlung in einer Kollektion mit Hilfe von . Ich habe Filme schon immer geliebt und sogar ein Unternehmen gegrĂŒndet, das in diesem Bereich tĂ€tig war, allerdings schon vor einer Weile. Es war schwierig, das Unternehmen als cool zu bezeichnen, aber die App â ja!
Wir erinnern daran: alle Leser von âHabrâ erhalten einen Rabatt von 10.000 Rubel bei der Anmeldung zu einem beliebigen Kurs von Skillbox mit dem Aktionscode âHabrâ.
Skillbox empfiehlt: Online-Kurs .
Also, was kann MovieSwiftUI?
- Es interagiert mit APIs â das macht fast jede moderne Anwendung.
- Es lĂ€dt asynchrone Daten ĂŒber Abfragen herunter und parst JSON in Swift-Modelle unter Verwendung von .
- Es zeigt Bilder, die auf Anfrage heruntergeladen werden, und cached sie.
- Diese Anwendung fĂŒr iOS, iPadOS und macOS bietet das beste UX fĂŒr die Nutzer dieser Betriebssysteme.
- Der Benutzer kann Daten generieren und eigene Film-Listen erstellen. Die Anwendung speichert und stellt benutzerspezifische Daten wieder her.
- Ansichten, Komponenten und Modelle sind klar durch das Redux-Muster getrennt. Der Datenstrom ist hier unidirektional. Er kann vollstĂ€ndig zwischengespeichert, wiederhergestellt und ĂŒberschrieben werden.
- Die Anwendung nutzt grundlegende SwiftUI-Komponenten, wie TabbedView, SegmentedControl, NavigationView, Form, Modal usw. AuĂerdem bietet sie benutzerdefinierte Ansichten, Gesten, UI/UX.

TatsĂ€chlich ist die Animation flĂŒssig, das GIF ist etwas ruckartig.
Die Arbeit an der Anwendung hat mir viel Erfahrung gebracht, und insgesamt ist es eine positive Erfahrung. Ich konnte eine voll funktionsfÀhige Anwendung schreiben, die ich im September verbessern und im App Store veröffentlichen werde, zeitgleich mit dem Release von iOS 13.
Redux, BindableObject und EnvironmentObject

Momentan arbeite ich seit etwa zwei Jahren mit Redux, also kenne ich mich relativ gut damit aus. Insbesondere verwende ich es im Frontend fĂŒr Webseiten sowie fĂŒr die Entwicklung von nativen iOS (Swift) und Android (Kotlin) Anwendungen.
Ich habe nie bereut, Redux als Architektur fĂŒr den Datenfluss bei der Erstellung einer Anwendung mit SwiftUI gewĂ€hlt zu haben. Die schwierigsten Momente bei der Verwendung von Redux in einer UIKit-Anwendung sind die Arbeit mit dem Store sowie das Abrufen und Extrahieren von Daten und deren Zuordnung zu Ihren Views / Komponenten. DafĂŒr musste ich eine Art Bibliothek von Connectoren (auf Basis von ReSwift und ReKotlin) erstellen. Funktioniert gut, aber es ist ziemlich viel Code. Leider ist er (noch) nicht Open Source.
Gute Nachrichten! Das einzige, worĂŒber man sich mit SwiftUI Gedanken machen sollte, wenn man plant, Redux zu verwenden, sind der Store, der Zustand und die Reducer. SwiftUI kĂŒmmert sich vollstĂ€ndig um die Interaktion mit dem Store dank @EnvironmentObject. Der Store beginnt also mit BindableObject.
Ich habe ein einfaches Swift-Paket erstellt, , das die grundlegende Verwendung von Redux ermöglicht. In meinem Fall ist es Teil von MovieSwiftUI. AuĂerdem habe ich , das hilft, diese Komponente zu verwenden.
Wie funktioniert das?
final public class Store: BindableObject {
public let willChange = PassthroughSubject()
private(set) public var state: State
private func _dispatch(action: Action) {
willChange.send()
state = reducer(state, action)
}
}Jedes Mal, wenn Sie eine Aktion ausfĂŒhren, aktivieren Sie den Reducer. Dieser bewertet die Aktionen basierend auf dem aktuellen Zustand der Anwendung. AnschlieĂend gibt er einen neuen modifizierten Zustand gemÀà dem Aktionstyp und den Daten zurĂŒck.
Da der Store ein BindableObject ist, wird er SwiftUI ĂŒber die Ănderung seines Wertes benachrichtigen, indem er die willChange-Eigenschaft verwendet, die von PassthroughSubject bereitgestellt wird. Das liegt daran, dass ein BindableObject einen PublisherType bereitstellen muss, die Implementierung des Protokolls jedoch fĂŒr dessen Verwaltung verantwortlich ist. Insgesamt ist es ein sehr leistungsfĂ€higes Werkzeug von Apple. Folglich wird SwiftUI im nĂ€chsten Renderzyklus helfen, den Body der Views entsprechend der ZustandsĂ€nderung darzustellen.
Das ist im Wesentlichen das Herz und die Magie von SwiftUI. In jeder View, die auf den Zustand abonniert ist, wird die View entsprechend den Daten, die aus dem Zustand abgerufen werden, und was sich geÀndert hat, angezeigt.
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let controller = UIHostingController(rootView: HomeView().environmentObject(store))
window.rootViewController = controller
self.window = window
window.makeKeyAndVisible()
}
}
}
struct CustomListCoverRow : View {
@EnvironmentObject var store: Store
let movieId: Int
var movie: Movie! {
return store.state.moviesState.movies[movieId]
}
var body: some View {
HStack(alignment: .center, spacing: 0) {
Image(movie.poster)
}.listRowInsets(EdgeInsets())
}
}Der Store wird beim Start der Anwendung als EnvironmentObject eingefĂŒgt und ist dann in jeder Ansicht ĂŒber @EnvironmentObject verfĂŒgbar. Die Leistung wird nicht beeintrĂ€chtigt, da abgeleitete Eigenschaften schnell aus dem Anwendungszustand abgerufen oder berechnet werden.
Der oben angegebene Code Àndert das Bild, wenn sich das Poster des Films Àndert.
Und das geschieht tatsÀchlich mit nur einer Zeile, die die Ansichten mit dem Zustand verbindet. Wenn Sie mit ReSwift auf iOS oder sogar mit React gearbeitet haben, werden Sie den Zauber von SwiftUI verstehen.
Jetzt können Sie versuchen, eine Aktion auszulösen und einen neuen Zustand zu veröffentlichen. Hier ist ein komplizierteres Beispiel.
struct CustomListDetail : View {
@EnvironmentObject var store: Store
let listId: Int
var list: CustomList {
store.state.moviesState.customLists[listId]!
}
var movies: [Int] {
list.movies.sortedMoviesIds(by: .byReleaseDate, state: store.state)
}
var body: some View {
List {
ForEach(movies) { movie in
NavigationLink(destination: MovieDetail(movieId: movie).environmentObject(self.store)) {
MovieRow(movieId: movie, displayListImage: false)
}
}.onDelete { (index) in
self.store.dispatch(action: MoviesActions.RemoveMovieFromCustomList(list: self.listId, movie: self.movies[index.first!]))
}
}
}
}Im obigen Code verwende ich die .onDelete-Aktion von SwiftUI fĂŒr jedes Element. Dies ermöglicht es einer Zeile in der Liste, das standardmĂ€Ăige iOS-Wischverfahren zum Löschen anzuzeigen. Wenn der Benutzer also auf die Löschtaste tippt, wird die entsprechende Aktion ausgefĂŒhrt und der Film wird aus der Liste entfernt.
Da die Eigenschaft der Liste vom BindableObject-Zustand abgeleitet und als EnvironmentObject eingefĂŒgt wird, aktualisiert SwiftUI die Liste, da ForEach mit der berechneten Eigenschaft movies verbunden ist.
Hier ist ein Teil des Reducers MoviesState:
func moviesStateReducer(state: MoviesState, action: Action) -> MoviesState {
var state = state
switch action {
// andere Aktionen.
case let action as MoviesActions.AddMovieToCustomList:
state.customLists[action.list]?.movies.append(action.movie)
case let action as MoviesActions.RemoveMovieFromCustomList:
state.customLists[action.list]?.movies.removeAll{ $0 == action.movie }
default:
break
}
return state
}Der Reducer wird ausgefĂŒhrt, wenn Sie eine Aktion senden und einen neuen Zustand zurĂŒckgeben, wie oben beschrieben.
Ich möchte nicht ins Detail gehen, woher SwiftUI tatsĂ€chlich weiĂ, was anzuzeigen ist. Um dies tiefer zu verstehen, ist es sinnvoll, in SwiftUI anzusehen. Dort wird auch ausfĂŒhrlich erklĂ€rt, warum und wann man , @Binding, ObjectBinding und EnvironmentObject verwenden sollte.
Skillbox empfiehlt:
- Praktischer Kurs .
- Angewandter Online-Kurs .
- ZweijÀhriger praktischer Kurs .
Quelle: habr.com
