Programų kūrimas naudojant SwiftUI. 1 dalis: Duomenų srautas ir Redux

Programų kūrimas naudojant SwiftUI. 1 dalis: Duomenų srautas ir Redux

Po to, kai dalyvavau WWDC 2019 sesijoje apie Sąjungos padėtį, nusprendžiau giliai pasinerti į SwiftUI. Dirbdamas su juo praleidau daug laiko ir dabar pradėjau kurti tikrą programą, kuri gali būti naudinga daugeliui vartotojų.

Aš tai pavadinau MovieSwiftUI - tai programa, skirta ieškoti naujų ir senų filmų, taip pat rinkti juos į kolekciją naudojant TMDB API. Visada mėgau filmus ir net sukūriau šioje srityje dirbančią kompaniją, nors ir seniai. Kompaniją vargu ar būtų galima pavadinti šaunia, bet paraiška buvo!

Primename: visiems „Habr“ skaitytojams – 10 000 rublių nuolaida užsiregistravus į bet kurį „Skillbox“ kursą naudojant „Habr“ reklamos kodą.

„Skillbox“ rekomenduoja: Mokomasis internetinis kursas „Java kūrėjo profesija“.

Taigi, ką gali padaryti MovieSwiftUI?

  • Sąveika su API – tai daro beveik bet kuri šiuolaikinė programa.
  • Įkelia asinchroninius užklausų duomenis ir analizuoja JSON į Swift modelį naudojant Koduojamas.
  • Rodo vaizdus, ​​įkeltus pagal užklausą, ir išsaugo juos talpykloje.
  • Ši programa, skirta „iOS“, „iPadOS“ ir „MacOS“, suteikia geriausią UX šių OS naudotojams.
  • Vartotojas gali generuoti duomenis ir kurti savo filmų sąrašus. Programa išsaugo ir atkuria vartotojo duomenis.
  • Vaizdai, komponentai ir modeliai yra aiškiai atskirti naudojant Redux šabloną. Duomenų srautas čia yra vienakryptis. Jį galima visiškai išsaugoti talpykloje, atkurti ir perrašyti.
  • Programa naudoja pagrindinius „SwiftUI“, „TabbedView“, „SegmentedControl“, „NavigationView“, „Form“, „Modal“ ir kt. Taip pat pateikiami pasirinktiniai vaizdai, gestai, vartotojo sąsaja / UX.

Programų kūrimas naudojant SwiftUI. 1 dalis: Duomenų srautas ir Redux
Tiesą sakant, animacija yra sklandi, GIF pasirodė šiek tiek trūkčiojantis

Darbas su programa man suteikė daug patirties ir apskritai tai buvo teigiama patirtis. Man pavyko parašyti pilnai veikiančią aplikaciją, rugsėjį ją patobulinsiu ir paskelbsiu AppStore, kartu su iOS 13 išleidimu.

Redux, BindableObject ir EnvironmentObject

Programų kūrimas naudojant SwiftUI. 1 dalis: Duomenų srautas ir Redux

Su Redux dirbu jau apie dvejus metus, todėl gana gerai jį išmanau. Visų pirma, aš naudoju jį priekinėje dalyje Reaguoti svetainėje, taip pat vietinėms iOS (Swift) ir Android (Kotlin) programoms kurti.

Niekada nesigailėjau pasirinkęs Redux kaip duomenų srauto architektūrą kuriant SwiftUI programą. Sudėtingiausios dalys naudojant „Redux“ UIKit programoje yra darbas su parduotuve, duomenų gavimas ir gavimas bei jų susiejimas su jūsų rodiniais / komponentais. Norėdami tai padaryti, turėjau sukurti savotišką jungčių biblioteką (naudodamas ReSwift ir ReKotlin). Veikia gerai, bet gana daug kodo. Deja, tai (dar) nėra atvirojo kodo.

Geros naujienos! Vieninteliai dalykai, dėl kurių reikia nerimauti naudojant „SwiftUI“, jei planuojate naudoti „Redux“, yra parduotuvės, būsenos ir reduktoriai. „@EnvironmentObject“ dėka sąveika su parduotuve visiškai pasirūpina „SwiftUI“. Taigi, parduotuvė prasideda nuo BindableObject.

Sukūriau paprastą Swift paketą, SwiftUIFlux, kuri suteikia pagrindinį Redux naudojimą. Mano atveju tai yra MovieSwiftUI dalis. aš taip pat parašė žingsnis po žingsnio pamoką, kuris padės jums naudoti šį komponentą.

Kaip tai veikia?

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

Kiekvieną kartą, kai suaktyvinate veiksmą, įjungiate pavarų dėžę. Jis įvertins veiksmus pagal esamą programos būseną. Tada ji grąžins naują pakeistą būseną pagal veiksmo tipą ir duomenis.

Na, kadangi parduotuvė yra BindableObject, ji praneš SwiftUI, kai pasikeis jos vertė, naudodama PassthroughSubject pateiktą nuosavybę willChange. Taip yra todėl, kad „BindableObject“ turi pateikti „PublisherType“, tačiau už jo valdymą atsakingas protokolo įgyvendinimas. Apskritai tai yra labai galingas „Apple“ įrankis. Atitinkamai, kitame atvaizdavimo cikle „SwiftUI“ padės pateikti rodinių turinį pagal būsenos pasikeitimą.

Tiesą sakant, tai yra visa „SwiftUI“ širdis ir magija. Dabar bet kuriame būseną prenumeruojančiame rodinyje vaizdas bus pateikiamas pagal tai, kokie duomenys iš valstybės gauti ir kas pasikeitė.

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

Parduotuvė įvedama kaip aplinkos objektas, kai programa paleidžiama, tada ji pasiekiama bet kuriame rodinyje naudojant @EnvironmentObject. Nėra našumo nuobaudos, nes išvestinės savybės greitai nuskaitomos arba apskaičiuojamos iš programos būsenos.

Aukščiau pateiktas kodas pakeičia vaizdą, jei pasikeičia filmo plakatas.

Ir tai iš tikrųjų daroma tik viena linija, kurios pagalba pažiūros susiejamos su valstybe. Jei dirbote su ReSwift iOS ar net prisijungti Naudodami „React“ suprasite „SwiftUI“ magiją.

Dabar galite pabandyti suaktyvinti veiksmą ir paskelbti naują būseną. Štai sudėtingesnis pavyzdys.

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

Aukščiau pateiktame kode naudoju .onDelete veiksmą iš SwiftUI kiekvienam IP. Tai leidžia sąrašo eilutėje rodyti įprastą „iOS“ braukimą, kad ištrintumėte. Taigi, kai vartotojas paliečia trynimo mygtuką, jis suaktyvina atitinkamą veiksmą ir pašalina filmą iš sąrašo.

Na, kadangi sąrašo ypatybė yra gauta iš BindableObject būsenos ir įvedama kaip EnvironmentObject, SwiftUI atnaujina sąrašą, nes ForEach yra susietas su filmų apskaičiuota ypatybe.

Čia yra „MoviesState“ reduktoriaus dalis:

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
}

Reduktorius vykdomas, kai siunčiate veiksmą ir grąžinate naują būseną, kaip nurodyta aukščiau.

Kol kas nesigilinsiu į detales – kaip SwiftUI iš tikrųjų žino, ką rodyti. Norint tai suprasti giliau, verta peržiūrėti WWDC sesiją apie duomenų srautą „SwiftUI“. Taip pat išsamiai paaiškinama, kodėl ir kada naudoti Valstybės, @Binding, ObjectBinding ir EnvironmentObject.

„Skillbox“ rekomenduoja:

Šaltinis: www.habr.com

Добавить комментарий