Toepassingsontwikkeling op SwiftUI. Deel 1: Datavloei en Redux

Toepassingsontwikkeling op SwiftUI. Deel 1: Datavloei en Redux

Nadat ek aan die State of the Union-sessie by WWDC 2019 deelgeneem het, het ek besluit om SwiftUI in detail te bestudeer. Ek het baie tyd daaraan spandeer en het nou begin om 'n regte toepassing te ontwikkel wat nuttig kan wees vir 'n wye verskeidenheid gebruikers.

Ek het dit MovieSwiftUI genoem - dit is 'n toepassing om nuwe en ou flieks te vind, sowel as om dit in 'n versameling te versamel met TMDB API. Ek was nog altyd lief vir rolprente en het selfs 'n maatskappy geskep wat in hierdie area werk, hoewel lank gelede. Dit was moeilik om die maatskappy cool te noem, maar die toepassing - ja!

Ons herinner: vir alle lesers van "Habr" - 'n afslag van 10 000 roebels wanneer u inskryf vir enige Skillbox-kursus met behulp van die "Habr"-promosiekode.

Skillbox beveel aan: Opvoedkundige aanlyn kursus "Beroep Java Ontwikkelaar".

So, wat doen MovieSwiftUI?

  • Interaksie met API - dit is wat byna enige moderne toepassing doen.
  • Laai asynchrone data op versoek en ontleed JSON in 'n Swift-model met behulp van Kodeerbaar.
  • Wys prente wat op aanvraag gelaai is en kas hulle.
  • Hierdie toepassing vir iOS, iPadOS en macOS bied die beste gebruikerservaring vir daardie bedryfstelselgebruikers.
  • Die gebruiker kan data genereer, hul eie flieklyste skep. Die toepassing stoor en herstel gebruikersdata.
  • Aansigte, komponente en modelle word duidelik geskei deur die Redux-patroon te gebruik. Die datavloei hier is eenrigting. Dit kan volledig gekas, herstel en oorskryf word.
  • Die toepassing gebruik die basiese komponente van SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal, ens. Dit bied ook pasgemaakte aansigte, gebare, UI/UX.

Toepassingsontwikkeling op SwiftUI. Deel 1: Datavloei en Redux
Trouens, die animasie is glad, die GIF het 'n bietjie kronkelrig geblyk te wees

Om aan die toepassing te werk het my baie ervaring gegee en in die algemeen is dit 'n positiewe ervaring. Ek kon 'n volledig funksionele toepassing skryf, in September sal ek dit verbeter en op die AppStore plaas, gelyktydig met die vrystelling van iOS 13.

Redux, BindableObject en EnvironmentObject

Toepassingsontwikkeling op SwiftUI. Deel 1: Datavloei en Redux

Ek werk nou al sowat twee jaar met Redux, so ek is relatief goed daarmee. In die besonder, ek gebruik dit in die frontend vir reageer webwerf, sowel as vir die ontwikkeling van inheemse iOS (Swift) en Android (Kotlin) toepassings.

Ek was nog nooit een keer spyt dat ek Redux as my datavloei-argitektuur gekies het om 'n SwiftUI-toepassing te bou nie. Die moeilikste deel van die gebruik van Redux in 'n UIKit-toepassing is om die winkel te hanteer en data te kry en te herwin en dit na jou aansigte/komponente te karteer. Om dit te doen, moes ek 'n soort koppelbiblioteek skep (op ReSwift en ReKotlin). Werk goed, maar nogal baie kode. Ongelukkig is dit (nog) nie oopbron nie.

Goeie nuus! Die enigste ding om oor bekommerd te wees met SwiftUI - as jy van plan is om Redux te gebruik - is die winkel, state en verkleiners. Interaksie met die winkel word heeltemal oorgeneem deur SwiftUI danksy @EnvironmentObject. So winkel begin met BindableObject.

Ek het 'n eenvoudige Swift-pakket geskep, SwiftUIFlux, wat basiese gebruik van Redux bied. In my geval is dit deel van MovieSwiftUI. ek ook het 'n stap-vir-stap handleiding geskryfom jou te help om hierdie komponent te gebruik.

Hoe werk dit?

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 keer as jy 'n aksie afvuur, vuur jy die verkleiner af. Dit sal aksies evalueer volgens die huidige stand van die aansoek. Verder sal dit 'n nuwe gewysigde toestand in ooreenstemming met die tipe aksie en data terugstuur.

Wel, aangesien winkel 'n BindableObject is, sal dit SwiftUI in kennis stel wanneer die waarde daarvan verander het met behulp van die willChange-eienskap wat deur PassthroughSubject verskaf word. Dit is omdat die BindableObject 'n PublisherType moet verskaf, maar die protokolimplementering is daarvoor verantwoordelik om dit te bestuur. Al met al is dit 'n baie kragtige instrument van Apple. Gevolglik sal SwiftUI in die volgende weergawesiklus help om die liggaam van die sienings volgens die staatsverandering weer te gee.

Eintlik is dit alles - die hart en magie van SwiftUI. Nou in enige aansig wat op die staat ingeteken is, sal die aansig vertoon word volgens watter data van die staat ontvang is en wat verander het.

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

Die winkel word as 'n EnvironmentObject ingespuit by die aanvang van die toepassing en is dan beskikbaar vir enige aansig wat @EnvironmentObject gebruik. Daar is geen prestasieboete nie, want afgeleide eienskappe word vinnig herwin of bereken vanaf toepassingstatus.

Die kode hierbo verander die beeld as die fliekplakkaat verander.

En dit word eintlik met net een lyn gedoen, met behulp waarvan die standpunte aan die staat verbind word. As jy met ReSwift op iOS gewerk het of selfs verbind met React, sal jy verstaan ​​wat die magie van SwiftUI is.

En nou kan jy probeer om die aksie te aktiveer en 'n nuwe toestand te publiseer. Hier is 'n meer komplekse voorbeeld.

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

In die kode hierbo gebruik ek die .onDelete-aksie van SwiftUI vir elke IP. Dit laat die ry in die lys toe om 'n normale iOS-swiep te vertoon om uit te vee. So wanneer die gebruiker die delete-knoppie tik, aktiveer dit die ooreenstemmende aksie en verwyder die fliek van die lys.

Wel, aangesien die lys-eienskap afgelei is van die BindableObject-toestand en as 'n EnvironmentObject ingespuit word, dateer SwiftUI die lys op omdat ForEach gebind is aan die films-berekende eiendom.

Hier is 'n deel van die MoviesState verkleiner:

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
}

Die verkleiner word uitgevoer wanneer jy 'n aksie stuur en 'n nuwe toestand terugstuur, soos hierbo gesΓͺ.

Ek sal nog nie in besonderhede ingaan nie - hoe weet SwiftUI eintlik wat om te vertoon. Om dit dieper te verstaan, is dit die moeite werd bekyk WWDC-sessie oor datavloei in SwiftUI. Dit verduidelik ook in detail waarom en wanneer om te gebruik staat, @Binding, ObjectBinding en EnvironmentObject.

Skillbox beveel aan:

Bron: will.com

Voeg 'n opmerking