Sviluppu di l'applicazioni nantu à SwiftUI. Parte 1: Dataflow è Redux

Sviluppu di l'applicazioni nantu à SwiftUI. Parte 1: Dataflow è Redux

Dopu avè assistitu à a sessione di u Statu di l'Unione à a WWDC 2019, aghju decisu di fà una immersione profonda in SwiftUI. Aghju passatu assai tempu à travaglià cun ellu è avà cuminciatu à sviluppà una vera applicazione chì pò esse utile à una larga gamma di utilizatori.

L'aghju chjamatu MovieSwiftUI - questa hè una app per a ricerca di filmi novi è vechji, è ancu di cullà in una cullezzione cù API TMDB. Aghju sempre amatu i filmi è ancu creatu una cumpagnia chì travaglia in questu campu, ancu s'ellu hè assai tempu fà. A cumpagnia ùn pudia micca esse chjamata cool, ma l'applicazione era!

Ramintemu: per tutti i lettori di "Habr" - un scontu di 10 000 rubles quandu si iscrizzione in ogni cursu Skillbox cù u codice promozionale "Habr".

Skillbox consiglia: Corso educativu in linea "Professional Java Developer".

Allora chì pò fà MovieSwiftUI?

  • Interagisce cù l'API - quasi ogni applicazione muderna face questu.
  • Carica dati asincroni nantu à e dumande è analizza JSON in u mudellu Swift utilizendu Codable.
  • Mostra l'imaghjini caricate nantu à dumanda è li cache.
  • Questa app per iOS, iPadOS è macOS furnisce u megliu UX per l'utilizatori di questi OS.
  • L'utilizatore pò generà dati è creà a so propria lista di filmi. L'applicazione salva è restaurà i dati di l'utilizatori.
  • Viste, cumpunenti è mudelli sò chjaramente separati cù u mudellu Redux. U flussu di dati quì hè unidirezionale. Pò esse cumplettamente in cache, restauratu è sovrascrittu.
  • L'applicazione usa i cumpunenti basi di SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal, etc. Fornisce ancu viste persunalizate, gesti, UI / UX.

Sviluppu di l'applicazioni nantu à SwiftUI. Parte 1: Dataflow è Redux
In fatti, l'animazione hè liscia, u GIF hè diventatu un pocu saccu

U travagliu nantu à l'app m'hà datu assai sperienza è in generale hè stata una sperienza positiva. Puderaghju scrive una applicazione cumplettamente funziunale, in settembre l'aghju da migliurà è publicà in l'AppStore, simultaneamente cù a liberazione di iOS 13.

Redux, BindableObject è EnvironmentObject

Sviluppu di l'applicazioni nantu à SwiftUI. Parte 1: Dataflow è Redux

Aghju travagliatu cù Redux per circa dui anni, cusì sò abbastanza versatu in questu. In particulare, l'aghju utilizatu in u frontend per React situ web, è ancu per sviluppà applicazioni native iOS (Swift) è Android (Kotlin).

Ùn aghju mai lamentatu di sceglie Redux cum'è l'architettura di flussu di dati per custruisce una applicazione SwiftUI. E parte più sfida quandu si usa Redux in una app UIKit sò travagliendu cù u magazinu è uttene è ricuperà e dati è mappendu i vostri punti di vista / cumpunenti. Per fà questu, aghju avutu à creà un tipu di biblioteca di connettori (usendu ReSwift è ReKotlin). Funziona bè, ma assai codice. Sfortunatamente, ùn hè micca (ancora) open source.

Bona nutizia ! L'unicu cose da preoccupassi cù SwiftUI - se pensa à aduprà Redux - sò i magazzini, i stati è i riduttori. L'interazzione cù a tenda hè cumpletamente curata da SwiftUI grazia à @EnvironmentObject. Allora, a tenda principia cù un BindableObject.

Aghju creatu un pacchettu Swift simplice, SwiftUIFlux, chì furnisce l'usu basicu di Redux. In u mo casu, hè parte di MovieSwiftUI. anch'eiu hà scrittu un tutoriale passu per passu, chì vi aiuterà à aduprà stu cumpunente.

Cumu viaghja?

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

Ogni volta chì attivate una azzione, attivate u gearbox. Evaluerà l'azzioni secondu u statu attuale di l'applicazione. Tandu turnerà un novu statu mudificatu in cunfurmità cù u tipu d'azzione è i dati.

Ebbè, postu chì a tenda hè un BindableObject, notificà à SwiftUI quandu u so valore cambia cù a pruprietà willChange furnita da PassthroughSubject. Questu hè chì u BindableObject deve furnisce un PublisherType, ma l'implementazione di u protocolu hè rispunsevule per a gestione. In generale, questu hè un strumentu assai putente da Apple. In cunsiquenza, in u prossimu ciculu di rendering, SwiftUI aiuterà à rende u corpu di e viste secondu u cambiamentu di statu.

In realtà, questu hè tuttu u core è a magia di SwiftUI. Avà, in ogni vista chì sottumette à un statu, a vista serà resa secondu ciò chì dati hè ricevutu da u statu è ciò chì hà cambiatu.

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

U Store hè injected cum'è EnvironmentObject quandu l'applicazione principia è hè dopu accessibile in ogni vista cù @EnvironmentObject. Ùn ci hè micca penalità di rendiment perchè e proprietà derivate sò rapidamente recuperate o calculate da u statu di l'applicazione.

U codice sopra cambia l'imaghjini se u poster di film cambia.

È questu hè in realtà fattu cù una sola linea, cù l'aiutu di quali viste sò cunnessi à u statu. Sè avete travagliatu cù ReSwift in iOS o ancu cunnette cù React, capirete a magia di SwiftUI.

Avà pudete pruvà à attivà l'azzione è publicà u novu statu. Eccu un esempiu più cumplessu.

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 u codice sopra, aghju aduprà l'azzione .onDelete da SwiftUI per ogni IP. Questu permette à a fila in a lista per vede u normale iOS swipe per sguassà. Allora quandu l'utilizatore toccu u buttone di sguassà, attiva l'azzione currispondente è elimina u filmu da a lista.

Ebbè, postu chì a pruprietà di a lista hè derivata da u statu BindableObject è hè injected cum'è EnvironmentObject, SwiftUI aghjurnà a lista perchè ForEach hè assuciatu cù a pruprietà calculata di i filmi.

Eccu una parte di u riduttore 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
}

U riduttore hè eseguitu quandu spedite una azzione è rinviate un novu statu, cum'è dettu sopra.

Ùn entreraghju ancu in dettagli - cumu SwiftUI sapi veramente ciò chì mostra. Per capiscenu questu più profondamente, vale a pena vede a sessione WWDC nantu à u flussu di dati in SwiftUI. Spiega ancu in dettagliu perchè è quandu aduprà statu, @Binding, ObjectBinding è EnvironmentObject.

Skillbox consiglia:

Source: www.habr.com

Add a comment