Devlopman aplikasyon sou SwiftUI. Pati 1: Dataflow ak Redux

Devlopman aplikasyon sou SwiftUI. Pati 1: Dataflow ak Redux

Apre m te fin patisipe nan sesyon Eta Inyon an nan WWDC 2019, mwen te deside fè yon plonje byen fon nan SwiftUI. Mwen te pase anpil tan travay ak li epi kounye a mwen te kòmanse devlope yon aplikasyon reyèl ki ka itil nan yon pakèt itilizatè.

Mwen te rele li MovieSwiftUI - sa a se yon aplikasyon pou chèche fim nouvo ak ansyen, osi byen ke kolekte yo nan yon koleksyon lè l sèvi avèk TMDB API. Mwen te toujou renmen fim e menm te kreye yon konpayi k ap travay nan domèn sa a, menm si sa gen anpil tan. Konpayi an diman ka rele fre, men aplikasyon an te!

Nou raple: pou tout lektè "Habr" - yon rabè nan 10 rubles lè w ap enskri nan nenpòt kou Skillbox lè l sèvi avèk kòd pwomosyon "Habr".

Skillbox rekòmande: Kou edikatif sou entènèt "pwofesyon Java pwomotè".

Se konsa, kisa MovieSwiftUI ka fè?

  • Kouri ak API a - prèske nenpòt aplikasyon modèn fè sa.
  • Chaje done asynchrone sou demann epi analize JSON nan modèl Swift la lè l sèvi avèk Codable.
  • Montre imaj chaje sou demann ak kachèt yo.
  • Aplikasyon sa a pou iOS, iPadOS, ak macOS bay pi bon UX pou itilizatè OS sa yo.
  • Itilizatè a ka jenere done epi kreye pwòp lis fim yo. Aplikasyon an sove ak restore done itilizatè yo.
  • Vi, eleman ak modèl yo byen klè separe lè l sèvi avèk modèl Redux la. Koule done isit la se inidireksyon. Li ka konplètman kachèt, restore ak ranplase.
  • Aplikasyon an sèvi ak eleman debaz yo nan SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal, elatriye. Li bay tou opinyon koutim, jès, UI / UX.

Devlopman aplikasyon sou SwiftUI. Pati 1: Dataflow ak Redux
An reyalite, animasyon an se lis, GIF la te tounen soti yon ti kras saccadé

Travay sou app a te ban m anpil eksperyans e an jeneral se te yon eksperyans pozitif. Mwen te kapab ekri yon aplikasyon konplètman fonksyonèl, nan mwa septanm nan mwen pral amelyore li epi pibliye li nan AppStore a, ansanm ak lage iOS 13.

Redux, BindableObject ak EnvironmentObject

Devlopman aplikasyon sou SwiftUI. Pati 1: Dataflow ak Redux

Mwen te travay ak Redux pou apeprè de ane kounye a, kidonk mwen relativman byen vèrs nan li. An patikilye, mwen sèvi ak li nan frontend la pou Reyaji sit entènèt, osi byen ke pou devlope aplikasyon natif natal iOS (Swift) ak Android (Kotlin).

Mwen pa janm regrèt mwen chwazi Redux kòm achitekti koule done pou bati yon aplikasyon SwiftUI. Pati ki pi difisil yo lè w ap itilize Redux nan yon app UIKit yo ap travay ak magazen an epi jwenn ak rekipere done epi mete yo sou opinyon/konpozan ou yo. Pou fè sa, mwen te oblije kreye yon kalite bibliyotèk nan konektè (lè l sèvi avèk ReSwift ak ReKotlin). Travay byen, men byen anpil nan kòd. Malerezman, li pa (ankò) sous louvri.

Bon nouvel! Sèl bagay pou enkyete sou SwiftUI - si w gen plan pou itilize Redux - se magazen, eta, ak redukteur. Entèaksyon ak magazen an konplètman okipe pa SwiftUI gras a @EnvironmentObject. Se konsa, magazen kòmanse ak yon BindableObject.

Mwen te kreye yon senp pake Swift, SwiftUIFlux, ki bay itilizasyon debaz Redux. Nan ka mwen an, li fè pati MovieSwiftUI. mwen tou te ekri yon leson patikilye etap pa etap, ki pral ede w itilize eleman sa a.

Kouman li travay?

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

Chak fwa ou deklanche yon aksyon, ou aktive bwat la. Li pral evalye aksyon yo selon eta aktyèl aplikasyon an. Lè sa a, li pral retounen yon nouvo eta modifye an akò ak kalite aksyon an ak done.

Oke, depi magazen se yon BindableObject, li pral avize SwiftUI lè valè li yo chanje lè l sèvi avèk pwopriyete WillChange ki bay PassthroughSubject. Sa a se paske BindableObject a dwe bay yon PublisherType, men aplikasyon pwotokòl la responsab pou jere li. An jeneral, sa a se yon zouti trè pwisan soti nan Apple. An konsekans, nan pwochen sik rannman an, SwiftUI pral ede rann kò a nan opinyon yo dapre chanjman eta a.

Aktyèlman, sa a se tout kè ak maji SwiftUI. Koulye a, nan nenpòt View ki abònman nan yon eta, gade nan pral rann dapre sa ki done yo resevwa nan men eta a ak sa ki chanje.

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

Magazen an sou fòm piki kòm yon EnvironmentObject lè aplikasyon an kòmanse epi li aksesib nan nenpòt View lè l sèvi avèk @EnvironmentObject. Pa gen okenn penalite pèfòmans paske pwopriyete ki sòti yo byen vit rekipere oswa kalkile nan eta aplikasyon an.

Kòd ki anwo a chanje imaj la si afich fim nan chanje.

Ak sa a se aktyèlman fè ak jis yon liy, avèk èd nan ki opinyon yo konekte ak eta a. Si ou te travay ak ReSwift sou iOS oswa menm konekte ak React, ou pral konprann majik SwiftUI.

Koulye a, ou ka eseye aktive aksyon an epi pibliye nouvo eta a. Men yon egzanp ki pi konplèks.

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

Nan kòd ki pi wo a, mwen itilize aksyon .onDelete nan SwiftUI pou chak IP. Sa a pèmèt ranje a nan lis la montre nòmal iOS glise nan efase. Se konsa, lè itilizatè a manyen bouton an efase, li deklanche aksyon ki koresponn lan epi retire fim nan nan lis la.

Oke, depi pwopriyete lis la sòti nan eta BindableObject epi li enjekte kòm yon EnvironmentObject, SwiftUI mete ajou lis la paske ForEach asosye ak pwopriyete yo kalkile fim.

Isit la se yon pati nan redukteur MoviesState la:

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
}

Se redukteur a egzekite lè ou voye yon aksyon epi retounen yon nouvo eta, jan sa endike anwo a.

Mwen pa pral antre nan detay ankò - ki jan SwiftUI aktyèlman konnen ki sa yo montre. Pou w konprann sa a pi pwofondman, li vo gade sesyon WWDC sou koule done nan SwiftUI. Li eksplike tou an detay poukisa ak ki lè yo itilize eta, @Binding, ObjectBinding ak EnvironmentObject.

Skillbox rekòmande:

Sous: www.habr.com

Add nouvo kòmantè