Rakenduse arendamine SwiftUI-s. Osa 1: andmevoog ja Redux

Rakenduse arendamine SwiftUI-s. Osa 1: andmevoog ja Redux

Pärast osalemist WWDC 2019 State of the Union sessioonis otsustasin põhjalikult uurida SwiftUI-d. Olen selle kallal palju aega veetnud ja nüüd olen alustanud reaalse rakenduse arendamist, mis võib olla kasulik laiale kasutajaskonnale.

Nimetasin selle MovieSwiftUI-ks — see on rakendus uute ja vanade filmide otsimiseks ning nende kogumiseks kollektsiooni kaudu TMDB API. Olen alati armastanud filme ja isegi loonud ettevõtte, mis tegutseb selles valdkonnas, kuigi see oli juba ammu. Ettevõtet oli raske nimetada ägedaks, aga rakendust küll!

Tuletame meelde: kõigile «Habr» lugejatele – 10 000 rubla allahindlus igale Skillboxi kursusele, kasutades sooduskoodi «Habr».

Skillbox soovitab: Haridusalane veebikursus «Java-arendaja amet».

Nii et, mida MovieSwiftUI suudab?

  • Seda integreeritakse API-ga — seda teeb peaaegu iga kaasaegne rakendus.
  • See laadib asünkroonselt andmeid päringute kaudu ja parsib JSON-i Swift mudelitesse, kasutades Codable.
  • Näitab pilte, mis on nõudmisel laaditud ja vahemälustatu.
  • See rakendus iOS, iPadOS ja macOS platvormidele tagab parima kasutajakogemuse nende operatsioonisüsteemide kasutajatele.
  • Kasutaja saab genereerida andmeid, luua oma filmide loendeid. Rakendus salvestab ja taastab kasutajate andmed.
  • Vaated, komponendid ja mudelid on selgelt eraldatud Redux-mustriga. Andmevoog on siin ühesuunaline. Seda saab täielikult vahemälustada, taastada ja ümber kirjutada.
  • Rakendus kasutab põhilisi SwiftUI komponente, sealhulgas TabbedView, SegmentedControl, NavigationView, Form, Modal jne. Samuti tagab see kohandatud vaated, žestid, UI/UX.

Rakenduse arendamine SwiftUI-s. Osa 1: andmevoog ja Redux
Tegelikult on animatsioon sujuv, kuid gif on natuke hüplev.

Rakenduse arendamine andis mulle palju kogemusi ja tervikuna oli see positiivne kogemus. Suutsin kirjutada täisfunktsionaalse rakenduse, mille parandan ja laon AppStore'i septembriks koos iOS 13.Väljalaskega.

Redux, BindableObject ja EnvironmentObject

Rakenduse arendamine SwiftUI-s. Osa 1: andmevoog ja Redux

Käesoleval hetkel olen töötanud Reduxiga juba umbes kaks aastat, seega mõistan seda suhteliselt hästi. Eelkõige kasutan seda oma veebilehe frontendis React , samuti kohalike iOS (Swift) ja Android (Kotlin) rakenduste arendamiseks.

Ma ei ole kordagi kahetsenud, et valisin Reduxi andmevoo arhitektuuriks SwiftUI rakenduse loomisel. Kõige keerulisemad hetked Reduxi kasutamisel UIKit rakenduses on tööstore'iga, samuti andmete saamine ja väljatoomine ning nende sidumine teie vaadete / komponentidega. Selle jaoks pidin looma omamoodi konnektorite teegi (ReSwift ja ReKotlin). See töötab hästi, kuid koodi on üsna palju. Kahjuks ei ole see (veel) avatud lähtekoodiga.

Hea uudis! Ainus, mille pärast tasub SwiftUI puhul muretseda, on see, kui plaanite kasutada Reduxi — store, olekud ja reducerid. SwiftUI hoolitseb store'i interaktsiooni täielikult @EnvironmentObject'i kaudu. Nii algab store BindableObject'iga.

Ma lõin lihtsa Swift paketi, SwiftUIFlux, mis tagab Reduxi põhikasutuse. Minu puhul on see osa MovieSwiftUI-st. Samuti olen kirjutanud samm-sammult õpetuse, mis aitab seda komponenti kasutada.

Kuidas see töötab?

final public class Store: BindableObject {
    public let willChange = PassthroughSubject()
        
    private(set) public var state: State
    
    private func _dispatch(action: Action) {
        willChange.send()
        state = reducer(state, action)
    }
}

Iga kord, kui käivitate tegevuse, aktiveerite redutseerija. See hindab tegevusi vastavalt rakenduse praegusele olekule. Talle järgneb uue muudetud oleku tagastamine vastavalt tegevuse tüübile ja andmetele.

Ja kuna store on BindableObject, teavitab ta SwiftUI-d oma väärtuse muutumisest, kasutades willChange'i omadust, mille pakub PassthroughSubject. See on nii, kuna BindableObject peab pakkuma PublisherType'i, kuid protokolli elluviimine vastutab selle haldamise eest. Üldiselt on see Apple'i väga võimas tööriist. Vastavalt sellele aitab SwiftUI järgmises renderdamise tsüklis kuvada vaadete keha oleku muutumise järgi.

Sisuliselt on see kõik — SwiftUI süda ja maagia. Nüüd igas vaates, mis on registreeritud oleku järgi, kuvatakse vaade vastavalt andmetele, mis on saadud olekust ja mis on muutunud.

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

Store on tõuk objektina rakenduse käivitamisel ja seejärel on see saadaval igas vaates @EnvironmentObject abil. Tõhusus ei kannata, kuna tuletatud omadused ekstraheeritakse või arvutatakse rakenduse olekust kiiresti.

Ülaltoodud kood muudab pilti, kui filmi plakat muutub.

Ja see toimub tegelikult vaid ühe reaga, millega seotakse vaated olekuga. Kui olete töötanud ReSwiftiga iOS-is või isegi connect Reactis, mõistate, mis on SwiftUI võlu.

Nüüd saab proovida aktiveerida toimingut ja avaldada uut olekut. Siin on keerulisem näide.

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

Ülaltoodud koodis kasutan SwiftUI-s iga IP jaoks tegevust .onDelete. See võimaldab loendi real näidata tavalist iOS-i pühkimisfunktsiooni kustutamiseks. Seega, kui kasutaja puudutab kustutamisnuppu, käivitab ta vastava tegevuse ja eemaldab filmi loendist.

Kuna loendi omadus on tuletatud BindableObject olekust ja see on sisestatud kui EnvironmentObject, värskendab SwiftUI loendit, kuna ForEach on seotud arvutatava omadusega movies.

Siin on osa reducerist 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
}

Reduktor töötab, kui saadate tegevuse ja tagastab uue oleku, nagu eespool öeldud.

Ma ei hakka veel detailidesse laskuma — kust Swifti UI tegelikult teab, mida kuvada. Selle põhjalikumaks mõistmiseks tasub vaadata WWDC sessiooni andmevoogudest Swifti UI-s. Samuti räägitakse seal üksikasjalikult, miks ja millal kasutada State, @Binding, ObjectBinding ja EnvironmentObject.

Skillbox soovitab:

Allikas: habr.com

Osta usaldusväärne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid 🔥 Osta usaldusväärne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid - ProHoster