
Nach der Teilnahme an der State of the Union-Sitzung auf der WWDC 2019 beschloss ich, mich intensiver mit SwiftUI zu beschĂ€ftigen. Ich habe viel Zeit damit verbracht und beginne nun mit der Entwicklung einer echten Anwendung, die einer breiten Nutzergruppe nĂŒtzlich sein könnte.
Ich habe sie MovieSwiftUI genannt â eine App zur Suche nach aktuellen sowie klassischen Filmen und zum Sammeln dieser in einer Sammlung mit Hilfe von . Ich habe schon immer Filme geliebt und sogar ein Unternehmen in dieser Branche gegrĂŒndet, jedoch ist das schon lange her. Das Unternehmen war schwer als cool zu bezeichnen, aber die App â definitiv!
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-Bildungskurs .
Also, was kann MovieSwiftUI?
- Es interagiert mit APIs â das tun fast alle modernen Anwendungen.
- Ruft asynchrone Daten anhand von Anfragen ab und parst JSON in Swift-Modelle, indem es .
- verwendet. Es zeigt Bilder an, die upon request geladen werden, und speichert sie im Cache.
- Diese App fĂŒr iOS, iPadOS und macOS bietet die beste Benutzererfahrung fĂŒr Nutzer dieser Betriebssysteme.
- Der Nutzer kann Daten generieren und eigene Filmlisten erstellen. Die App speichert und stellt die Benutzerdaten wieder her.
- Darstellungen, Komponenten und Modelle sind klar durch das Redux-Muster getrennt. Der Datenfluss ist hier unidirektional. Er kann vollstĂ€ndig zwischengespeichert, wiederhergestellt und ĂŒberschrieben werden.
- Die Anwendung verwendet grundlegende SwiftUI-Komponenten wie TabbedView, SegmentedControl, NavigationView, Form, Modal usw. AuĂerdem bietet sie benutzerdefinierte Ansichten, Gesten sowie UI/UX.

Die Animation ist tatsĂ€chlich flĂŒssig, das GIF ist etwas ruckartig.
Die Arbeit an der Anwendung hat mir viel Erfahrung gebracht, insgesamt war 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 der Veröffentlichung von iOS 13.
Redux, BindableObject und EnvironmentObject

Ich arbeite nun seit etwa zwei Jahren mit Redux, sodass ich mich relativ gut damit auskenne. Insbesondere benutze ich es im Frontend fĂŒr die Website sowie zur Entwicklung nativer iOS (Swift) und Android (Kotlin) Anwendungen.
Ich habe meine Entscheidung fĂŒr Redux als Datenflussarchitektur zur Erstellung einer Anwendung in SwiftUI nie bereut. Die kompliziertesten Aspekte bei der Verwendung von Redux in einer UIKit-Anwendung sind die Arbeit mit dem Store sowie das Abrufen und Verarbeiten von Daten und deren Zuordnung zu Ihren Ansichten / Komponenten. Zu diesem Zweck musste ich eine Art von Connector-Bibliothek (basierend auf ReSwift und ReKotlin) erstellen. Es funktioniert gut, aber es erfordert ziemlich viel Code. Leider ist es (bis jetzt) nicht Open Source.
Gute Nachrichten! Das Einzige, worĂŒber man sich mit SwiftUI Gedanken machen sollte â sofern man plant, Redux zu verwenden â ist der Store, der Zustand und die Reducer. SwiftUI ĂŒbernimmt die Interaktion mit dem Store vollstĂ€ndig ĂŒber @EnvironmentObject. Somit beginnt der Store mit BindableObject.
Ich habe ein einfaches Swift-Paket erstellt, , das die grundlegende Nutzung von Redux ermöglicht. In meinem Fall ist es Teil von MovieSwiftUI. AuĂerdem habe ich , das Ihnen hilft, diese Komponente zu nutzen.
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. Er bewertet die Aktionen basierend auf dem aktuellen Zustand der Anwendung. AnschlieĂend gibt er einen neuen modifizierten Zustand zurĂŒck, der sich nach der Art der Aktion und den Daten richtet.
Da das Store ein BindableObject ist, wird es SwiftUI ĂŒber die Ănderung seines Wertes informieren, indem es die willChange-Eigenschaft nutzt, die von PassthroughSubject bereitgestellt wird. Dies ist notwendig, da ein BindableObject einen PublisherType bereitstellen muss, die Implementierung des Protokolls jedoch fĂŒr die Verwaltung verantwortlich ist. Insgesamt ist dies ein sehr leistungsfĂ€higes Werkzeug von Apple. Im nĂ€chsten Rendering-Zyklus hilft SwiftUI dann, den Inhalt der Ansichten entsprechend der Ănderung des Zustands darzustellen.
Das ist also die Essenz und die Magie von SwiftUI. Jetzt wird in jeder Ansicht, die auf den Zustand abonniert ist, die Darstellung entsprechend den Daten, die aus dem Zustand abgerufen werden, und den Ănderungen erfolgen.
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 integriert und ist dann in jeder Ansicht ĂŒber @EnvironmentObject verfĂŒgbar. Die Leistung bleibt unberĂŒhrt, da abgeleitete Eigenschaften schnell aus dem Anwendungszustand extrahiert 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 Verbindung der Ansichten mit dem Zustand herstellt. Wenn Sie mit ReSwift auf iOS oder sogar mit React gearbeitet haben, werden Sie die Magie von SwiftUI verstehen.
Jetzt können Sie versuchen, die Aktion zu aktivieren und einen neuen Status 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 Aktion .onDelete aus SwiftUI fĂŒr jede IP. Dies ermöglicht es der Zeile in der Liste, die gewohnte iOS-Wischbewegung zum Löschen anzuzeigen. Wenn der Benutzer auf die Löschtaste tippt, wird die entsprechende Aktion ausgelöst und der Film aus der Liste entfernt.
Da die Listen-Eigenschaft von BindableObject abgeleitet ist und als EnvironmentObject injiziert 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 den neuen Zustand zurĂŒckgeben, wie oben beschrieben.
Ich werde noch nicht ins Detail gehen â woher SwiftUI tatsĂ€chlich weiĂ, was angezeigt werden soll. Um dies tiefer zu verstehen, sollten Sie in SwiftUI ansehen. 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
