Forritaþróun á SwiftUI. Hluti 1: Dataflow og Redux

Forritaþróun á SwiftUI. Hluti 1: Dataflow og Redux

Eftir að hafa mætt á State of the Union fundinn á WWDC 2019 ákvað ég að kafa djúpt í SwiftUI. Ég hef eytt miklum tíma í að vinna með það og er nú byrjaður að þróa alvöru forrit sem getur nýst mörgum notendum.

Ég kallaði það MovieSwiftUI - þetta er app til að leita að nýjum og gömlum kvikmyndum, auk þess að safna þeim í safn með því að nota TMDB API. Ég hef alltaf elskað kvikmyndir og jafnvel stofnað fyrirtæki sem starfar á þessu sviði, þó fyrir löngu síðan. Fyrirtækið gæti varla kallast flott, en umsóknin var!

Við minnum á: fyrir alla Habr lesendur - 10 rúblur afsláttur þegar þú skráir þig á hvaða Skillbox námskeið sem er með því að nota Habr kynningarkóðann.

Skillbox mælir með: Fræðslunámskeið á netinu "Profession Java verktaki".

Svo hvað getur MovieSwiftUI gert?

  • Hefur samskipti við API - næstum öll nútíma forrit gera þetta.
  • Hleður ósamstilltum gögnum um beiðnir og flokkar JSON inn í Swift líkanið með því að nota Kóðanlegt.
  • Sýnir myndir hlaðnar eftir beiðni og vistar þær í skyndiminni.
  • Þetta app fyrir iOS, iPadOS og macOS veitir bestu UX fyrir notendur þessara stýrikerfi.
  • Notandinn getur búið til gögn og búið til sína eigin kvikmyndalista. Forritið vistar og endurheimtir notendagögn.
  • Útsýni, íhlutir og gerðir eru greinilega aðskilin með Redux mynstrinu. Gagnaflæðið hér er einátta. Það er hægt að geyma það að fullu, endurheimta og skrifa yfir.
  • Forritið notar grunnþætti SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal osfrv. Það býður einnig upp á sérsniðnar skoðanir, bendingar, UI/UX.

Forritaþróun á SwiftUI. Hluti 1: Dataflow og Redux
Reyndar er hreyfimyndin slétt, GIF-myndin reyndist svolítið skökk

Vinna við appið gaf mér mikla reynslu og í heildina var þetta jákvæð reynsla. Ég gat skrifað fullkomlega virkt forrit, í september mun ég bæta það og birta það í AppStore, samtímis útgáfu iOS 13.

Redux, BindableObject og EnvironmentObject

Forritaþróun á SwiftUI. Hluti 1: Dataflow og Redux

Ég hef verið að vinna með Redux í um tvö ár núna, þannig að ég er tiltölulega vel að mér í því. Sérstaklega nota ég það í framenda fyrir Bregðast vefsíðu, sem og til að þróa innfædd iOS (Swift) og Android (Kotlin) forrit.

Ég hef aldrei séð eftir því að hafa valið Redux sem gagnaflæðisarkitektúr til að byggja upp SwiftUI forrit. Það sem er mest krefjandi þegar Redux er notað í UIKit appi er að vinna með versluninni og sækja og sækja gögn og kortleggja þau að skoðunum/hlutum þínum. Til að gera þetta þurfti ég að búa til einskonar bókasafn af tengjum (með ReSwift og ReKotlin). Virkar vel, en frekar mikið af kóða. Því miður er það ekki (enn) opinn uppspretta.

Góðar fréttir! Það eina sem þarf að hafa áhyggjur af með SwiftUI - ef þú ætlar að nota Redux - eru verslanir, ríki og niðurfellingar. Samskipti við verslunina sjá algjörlega um af SwiftUI þökk sé @EnvironmentObject. Svo, verslun byrjar með BindableObject.

Ég bjó til einfaldan Swift pakka, SwiftUIFlux, sem veitir grunnnotkun á Redux. Í mínu tilfelli er það hluti af MovieSwiftUI. Ég líka skrifaði skref-fyrir-skref kennsluefni, sem mun hjálpa þér að nota þennan íhlut.

Hvernig virkar það?

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

Í hvert skipti sem þú kveikir á aðgerð virkjarðu gírkassann. Það mun meta aðgerðir í samræmi við núverandi stöðu umsóknarinnar. Það mun síðan skila nýju breyttu ástandi í samræmi við gerð aðgerða og gögn.

Jæja, þar sem verslun er BindableObject mun hún láta SwiftUI vita þegar gildi hennar breytist með því að nota willChange eignina sem PassthroughSubject gefur. Þetta er vegna þess að BindableObject verður að veita PublisherType, en innleiðing samskiptareglur ber ábyrgð á því að stjórna því. Á heildina litið er þetta mjög öflugt tæki frá Apple. Í samræmi við það, í næstu flutningslotu, mun SwiftUI hjálpa til við að birta meginmál skoðana í samræmi við ástandsbreytinguna.

Reyndar er þetta allt hjarta og töfrar SwiftUI. Nú, í hvaða útsýni sem er áskrifandi að ríki, verður útsýnið birt í samræmi við hvaða gögn berast frá ríkinu og hvað hefur breyst.

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

Verslunin er sprautuð inn sem EnvironmentObject þegar forritið byrjar og er síðan aðgengilegt á hvaða skjá sem er með @EnvironmentObject. Það er engin frammistöðu refsing vegna þess að afleiddar eiginleikar eru fljótt sóttir eða reiknaðir út frá umsóknarstöðu.

Kóðinn hér að ofan breytir myndinni ef kvikmyndaplakatið breytist.

Og þetta er í raun og veru gert með aðeins einni línu, með hjálp þeirrar skoðunar sem tengjast ríkinu. Ef þú hefur unnið með ReSwift á iOS eða jafnvel tengjast með React muntu skilja töfra SwiftUI.

Nú geturðu reynt að virkja aðgerðina og birt nýja stöðuna. Hér er flóknara dæmi.

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

Í kóðanum hér að ofan er ég að nota .onDelete aðgerðina frá SwiftUI fyrir hverja IP. Þetta gerir röðinni á listanum kleift að sýna venjulega iOS strjúka til að eyða. Þannig að þegar notandinn snertir eyðingarhnappinn kveikir það á samsvarandi aðgerð og fjarlægir kvikmyndina af listanum.

Jæja, þar sem listaeiginleikinn er fenginn frá BindableObject ástandinu og er sprautað inn sem EnvironmentObject, uppfærir SwiftUI listann vegna þess að ForEach er tengt við útreiknaðan eiginleika kvikmyndarinnar.

Hér er hluti af MoviesState reducer:

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
}

Minnkinn er keyrður þegar þú sendir aðgerð og skilar nýju ástandi, eins og fram kemur hér að ofan.

Ég mun ekki fara í smáatriði ennþá - hvernig SwiftUI veit í raun hvað á að birta. Til að skilja þetta dýpra er það þess virði skoða WWDC lotu um gagnaflæði í SwiftUI. Það útskýrir einnig í smáatriðum hvers vegna og hvenær á að nota State, @Binding, ObjectBinding og EnvironmentObject.

Skillbox mælir með:

Heimild: www.habr.com

Bæta við athugasemd