Applikaasjeûntwikkeling op SwiftUI. Diel 1: Dataflow en Redux

Applikaasjeûntwikkeling op SwiftUI. Diel 1: Dataflow en Redux

Nei it bywenjen fan 'e State of the Union-sesje by WWDC 2019, besleat ik in djippe dûk te nimmen yn SwiftUI. Ik haw der in protte tiid mei trochbrocht en bin no begon mei it ûntwikkeljen fan in echte applikaasje dy't nuttich kin wêze foar in breed skala oan brûkers.

Ik neamde it MovieSwiftUI - dit is in app foar it sykjen nei nije en âlde films, lykas se sammelje yn in samling mei TMDB API. Ik haw altyd fan films hâlden en sels in bedriuw makke dat op dit mêd wurket, hoewol in lange tiid lyn. It bedriuw koe amper cool neamd wurde, mar de applikaasje wie!

Wy herinnerje: foar alle lêzers fan "Habr" - in koarting fan 10 roebel by it ynskriuwen fan in Skillbox-kursus mei de promoasjekoade "Habr".

Skillbox advisearret: Edukative online kursus "Berop Java Developer".

Dus wat kin MovieSwiftUI dwaan?

  • Ynteraksje mei de API - hast elke moderne applikaasje docht dit.
  • Laadt asynchrone gegevens oer oanfragen en parseart JSON yn it Swift-model mei Kodabel.
  • Toant ôfbyldings laden op oanfraach en cache se.
  • Dizze app foar iOS, iPadOS en macOS biedt de bêste UX foar brûkers fan dizze OS's.
  • De brûker kin gegevens generearje en har eigen filmlisten meitsje. De applikaasje bewarret en herstelt brûkersgegevens.
  • Views, komponinten en modellen binne dúdlik skieden mei it Redux-patroan. De gegevensstream hjir is unidirectional. It kin folslein yn 'e cache, restaurearre en oerskreaun wurde.
  • De applikaasje brûkt de basiskomponinten fan SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal, ensfh. It biedt ek oanpaste werjeften, stjoerings, UI / UX.

Applikaasjeûntwikkeling op SwiftUI. Diel 1: Dataflow en Redux
Yn feite, de animaasje is glêd, de GIF draaide in bytsje jerky

It wurkjen oan de app joech my in protte ûnderfining en oer it algemien wie it in positive ûnderfining. Ik koe in folslein funksjonele applikaasje skriuwe, yn septimber sil ik it ferbetterje en publisearje yn 'e AppStore, tagelyk mei de frijlitting fan iOS 13.

Redux, BindableObject en EnvironmentObject

Applikaasjeûntwikkeling op SwiftUI. Diel 1: Dataflow en Redux

Ik wurkje no sa'n twa jier mei Redux, dus ik bin der relatyf goed yn. Benammen brûk ik it yn 'e frontend foar Reagearje webside, lykas ek foar it ûntwikkeljen fan native iOS (Swift) en Android (Kotlin) applikaasjes.

Ik haw der noait spyt fan makke dat ik Redux keas as de datastream-arsjitektuer foar it bouwen fan in SwiftUI-applikaasje. De meast útdaagjende dielen by it brûken fan Redux yn in UIKit-app wurkje mei de winkel en it krijen en opheljen fan gegevens en it yn kaart bringen fan jo werjeften / komponinten. Om dit te dwaan, moast ik in soarte fan bibleteek fan ferbinings meitsje (mei ReSwift en ReKotlin). Wurket goed, mar nochal in soad koade. Spitigernôch is it (noch) gjin iepen boarne.

Goed nijs! De iennichste dingen om jo soargen oer te meitsjen mei SwiftUI - as jo fan plan binne Redux te brûken - binne winkels, steaten en ferminderers. Ynteraksje mei de winkel wurdt folslein fersoarge troch SwiftUI tank oan @EnvironmentObject. Dat, winkel begjint mei in BindableObject.

Ik makke in ienfâldich Swift-pakket, SwiftUIFlux, dy't basisgebrûk fan Redux leveret. Yn myn gefal is it diel fan MovieSwiftUI. ik ek skreau in stap-foar-stap tutorial, dy't jo helpe sil dizze komponint brûke.

Hoe wurket it?

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)
    }
}

Elke kear as jo in aksje aktivearje, aktivearje jo de gearbox. It sil aksjes evaluearje neffens de hjoeddeistige tastân fan 'e applikaasje. It sil dan in nije wizige steat weromjaan yn oerienstimming mei it aksjetype en gegevens.

No, om't winkel in BindableObject is, sil it SwiftUI ynformearje as de wearde feroaret mei it willChange-eigenskip levere troch PassthroughSubject. Dit is om't it BindableObject in PublisherType moat leverje, mar de protokol-ymplemintaasje is ferantwurdlik foar it behearen. Oer it algemien is dit in heul krêftich ark fan Apple. Dêrnjonken sil SwiftUI yn 'e folgjende renderingssyklus helpe om it lichem fan' e werjeften te werjaan neffens de steatferoaring.

Eigentlik is dit al it hert en magy fan SwiftUI. No, yn elke werjefte dy't abonnearret op in steat, sil de werjefte wurde werjûn neffens hokker gegevens ûntfongen binne fan 'e steat en wat is feroare.

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())
    }
}

De winkel wurdt ynjeksje as in EnvironmentObject as de applikaasje begjint en is dan tagonklik yn elke werjefte mei @EnvironmentObject. Der is gjin prestaasje boete omdat ôflaat eigenskippen wurde fluch ophelle of berekkene út applikaasje steat.

De koade hjirboppe feroaret de ôfbylding as de filmposter feroaret.

En dat wurdt eins dien mei mar ien line, mei help fan werjeften wurde ferbûn mei de steat. As jo ​​hawwe wurke mei ReSwift op iOS of sels ferbine mei React sille jo de magy fan SwiftUI begripe.

No kinne jo besykje de aksje te aktivearjen en de nije steat te publisearjen. Hjir is in mear komplekse foarbyld.

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!]))
            }
        }
    }
}

Yn 'e koade hjirboppe brûk ik de .onDelete-aksje fan SwiftUI foar elke IP. Dit lit de rige yn 'e list de normale iOS-swipe werjaan om te wiskjen. Dus as de brûker de knop wiskje oanrekket, triggert it de oerienkommende aksje en ferwideret de film fan 'e list.

No, om't de listeigenskip is ôflaat fan 'e BindableObject-tastân en wurdt ynjeksje as in EnvironmentObject, bywurket SwiftUI de list om't ForEach is assosjearre mei de films berekkene eigendom.

Hjir is in diel fan 'e MoviesState-reduksjeur:

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
}

De reduksje wurdt útfierd as jo in aksje ferstjoere en in nije steat weromjaan, lykas hjirboppe neamd.

Ik sil noch net yn detail gean - hoe SwiftUI eins wit wat te werjaan. Om dit djipper te begripen, is it wurdich besjoch WWDC-sesje oer gegevensstream yn SwiftUI. It ferklearret ek yn detail wêrom en wannear te brûken Steat, @Binding, ObjectBinding en EnvironmentObject.

Skillbox advisearret:

Boarne: www.habr.com

Add a comment