Żvilupp ta' applikazzjoni fuq SwiftUI. Parti 1: Dataflow u Redux
Wara li attendejt is-sessjoni tal-Istat tal-Unjoni fil-WWDC 2019, iddeċidejt li ngħaddas fil-fond fis-SwiftUI. Għamilt ħafna ħin naħdem magħha u issa bdejt niżviluppa applikazzjoni reali li tista’ tkun utli għal firxa wiesgħa ta’ utenti.
Sejjaħt MovieSwiftUI - din hija app għat-tiftix għal films ġodda u qodma, kif ukoll biex tiġborhom f'kollezzjoni bl-użu TMDB API. Dejjem ħabbejt il-films u saħansitra ħloqt kumpanija li taħdem f’dan il-qasam, għalkemm żmien twil ilu. Il-kumpanija ma tantx setgħet tissejjaħ cool, iżda l-applikazzjoni kienet!
Infakkrukom:għall-qarrejja kollha ta '"Habr" - skont ta' 10 rublu meta tirreġistra fi kwalunkwe kors ta 'Skillbox billi tuża l-kodiċi promozzjonali "Habr".
Tinteraġixxi mal-API - kważi kull applikazzjoni moderna tagħmel dan.
Tgħabbi dejta asinkronika dwar it-talbiet u teżamina JSON fil-mudell Swift bl-użu Kodifikabbli.
Juri immaġini mgħobbija fuq talba u jżommhom fil-caches.
Din l-app għal iOS, iPadOS, u macOS tipprovdi l-aħjar UX għall-utenti ta’ dawn l-OS.
L-utent jista 'jiġġenera data u joħloq il-listi tal-films tiegħu stess. L-applikazzjoni tiffranka u tirrestawra d-dejta tal-utent.
Il-veduti, il-komponenti u l-mudelli huma separati b'mod ċar bl-użu tal-mudell Redux. Il-fluss tad-dejta hawnhekk huwa unidirezzjonali. Jista 'jiġi kompletament fil-cache, restawrat u miktub fuqu.
L-applikazzjoni tuża l-komponenti bażiċi ta 'SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal, eċċ. Jipprovdi wkoll veduti personalizzati, ġesti, UI/UX.
Fil-fatt, l-animazzjoni hija bla xkiel, il-GIF irriżulta ftit jerky
Il-ħidma fuq l-app tani ħafna esperjenza u b'mod ġenerali kienet esperjenza pożittiva. Stajt nikteb applikazzjoni kompletament funzjonali, f'Settembru ser intejjebha u nippubblikaha fl-AppStore, fl-istess ħin mar-rilaxx ta' iOS 13.
Redux, BindableObject u EnvironmentObject
Ilni naħdem ma' Redux għal madwar sentejn issa, għalhekk jien relattivament kapaċi sew fiha. B'mod partikolari, nużaha fil-frontend għal Irreaġixxi websajt, kif ukoll għall-iżvilupp ta’ applikazzjonijiet indiġeni ta’ iOS (Swift) u Android (Kotlin).
Qatt ma ddispjaċini li għażilt Redux bħala l-arkitettura tal-fluss tad-dejta għall-bini ta 'applikazzjoni SwiftUI. L-aktar partijiet ta’ sfida meta tuża Redux f’app UIKit qed jaħdmu mal-maħżen u jġibu u jġibu d-dejta u jimmappjawha mal-fehmiet/komponenti tiegħek. Biex tagħmel dan, kelli noħloq tip ta 'librerija ta' konnetturi (bl-użu ta 'ReSwift u ReKotlin). Jaħdem tajjeb, iżda pjuttost ħafna kodiċi. Sfortunatament, mhuwiex (għadu) sors miftuħ.
Aħbar tajba! L-uniċi affarijiet li għandek tinkwieta dwar SwiftUI - jekk qed tippjana li tuża Redux - huma l-ħwienet, l-istati u r-reducers. L-interazzjoni mal-maħżen hija kompletament ikkurata minn SwiftUI grazzi għal @EnvironmentObject. Allura, il-maħżen jibda b'BindableObject.
Ħloqt pakkett Swift sempliċi, SwiftUIFlux, li jipprovdi użu bażiku ta' Redux. Fil-każ tiegħi huwa parti minn MovieSwiftUI. Jien ukoll kiteb tutorja pass pass, li tgħinek tuża dan il-komponent.
Kif taħdem?
final public class Store<State: FluxState>: BindableObject {
public let willChange = PassthroughSubject<Void, Never>()
private(set) public var state: State
private func _dispatch(action: Action) {
willChange.send()
state = reducer(state, action)
}
}
Kull darba li tiskatta azzjoni, tattiva l-gearbox. Se tevalwa l-azzjonijiet skont l-istat attwali tal-applikazzjoni. Imbagħad se jirritorna stat modifikat ġdid skont it-tip ta 'azzjoni u d-dejta.
Ukoll, peress li l-maħżen huwa BindableObject, jinnotifika lil SwiftUI meta l-valur tiegħu jinbidel billi juża l-proprjetà willChange ipprovduta minn PassthroughSubject. Dan għaliex il-BindableObject għandu jipprovdi PublisherType, iżda l-implimentazzjoni tal-protokoll hija responsabbli għall-ġestjoni tiegħu. B'mod ġenerali, din hija għodda qawwija ħafna minn Apple. Għaldaqstant, fiċ-ċiklu ta 'rendi li jmiss, SwiftUI se jgħin biex jirrendi l-korp tal-fehmiet skont il-bidla tal-istat.
Fil-fatt, din hija l-qalba u l-maġija kollha ta 'SwiftUI. Issa, fi kwalunkwe ħsieb li jissottoskrivi għal stat, il-veduta se tingħata skond liema data tiġi riċevuta mill-istat u dak li nbidel.
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<AppState>
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())
}
}
Il-Aħżen jiġi injettat bħala EnvironmentObject meta tibda l-applikazzjoni u mbagħad ikun aċċessibbli fi kwalunkwe viżjoni bl-użu ta' @EnvironmentObject. M'hemm l-ebda penali tal-prestazzjoni minħabba li l-proprjetajiet derivati jiġu rkuprati jew ikkalkulati malajr mill-istat tal-applikazzjoni.
Il-kodiċi ta 'hawn fuq jibdel l-immaġni jekk il-poster tal-film jinbidel.
U dan fil-fatt isir b'linja waħda biss, li bl-għajnuna tagħha l-opinjonijiet huma konnessi mal-istat. Jekk ħdimt ma' ReSwift fuq iOS jew saħansitra jgħaqqdu ma 'React, int ser tifhem il-maġija ta' SwiftUI.
Issa tista 'tipprova tattiva l-azzjoni u tippubblika l-istat il-ġdid. Hawn eżempju aktar kumpless.
struct CustomListDetail : View {
@EnvironmentObject var store: Store<AppState>
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!]))
}
}
}
}
Fil-kodiċi ta 'hawn fuq, qed nuża l-azzjoni .onDelete minn SwiftUI għal kull IP. Dan jippermetti li r-ringiela fil-lista turi l-iSwipe normali tal-iOS biex tħassar. Allura meta l-utent imiss il-buttuna tħassar, iqajjem l-azzjoni korrispondenti u jneħħi l-film mil-lista.
Ukoll, peress li l-proprjetà tal-lista hija derivata mill-istat BindableObject u hija injettata bħala EnvironmentObject, SwiftUI taġġorna l-lista minħabba li ForEach hija assoċjata mal-proprjetà kkalkulata tal-films.
Hawn parti mir-reducer MoviesState:
func moviesStateReducer(state: MoviesState, action: Action) -> MoviesState {
var state = state
switch action {
// other actions.
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
}
Ir-reducer jiġi esegwit meta tibgħat azzjoni u tirritorna stat ġdid, kif intqal hawn fuq.
Għadni mhux se nidħol fid-dettall - kif SwiftUI fil-fatt jaf x'għandu juri. Biex tifhem dan aktar fil-fond, ta 'min ara sessjoni tal-WWDC dwar il-fluss tad-dejta fi SwiftUI. Jispjega wkoll fid-dettall għaliex u meta tuża Istat, @Binding, ObjectBinding u EnvironmentObject.