Հավելվածի մշակում SwiftUI-ում: Մաս 1. Dataflow և Redux

Հավելվածի մշակում SwiftUI-ում: Մաս 1. Dataflow և Redux

WWDC 2019-ում «The State of Union» նիստին մասնակցելուց հետո ես որոշեցի խորը սուզվել SwiftUI-ում: Ես շատ ժամանակ եմ ծախսել դրա հետ աշխատելու համար և այժմ սկսել եմ իրական հավելված մշակել, որը կարող է օգտակար լինել օգտատերերի լայն շրջանակի համար:

Ես այն անվանեցի MovieSwiftUI - սա նոր և հին ֆիլմեր որոնելու, ինչպես նաև դրանք հավաքածուի մեջ հավաքելու հավելված է՝ օգտագործելով TMDB API. Ես միշտ սիրել եմ ֆիլմեր և նույնիսկ ստեղծել եմ այս ոլորտում աշխատող ընկերություն, թեև շատ վաղուց։ Ընկերությունը դժվար թե կարելի է անվանել զովացուցիչ, բայց դիմումը եղել է:

Հիշեցում. «Habr»-ի բոլոր ընթերցողների համար՝ 10 ռուբլի զեղչ «Habr» գովազդային կոդով Skillbox-ի ցանկացած դասընթացին գրանցվելիս:

Skillbox-ը խորհուրդ է տալիս. Ուսումնական առցանց դասընթաց «Մասնագիտություն Java ծրագրավորող».

Այսպիսով, ի՞նչ կարող է անել MovieSwiftUI-ն:

  • Փոխազդում է API-ի հետ. գրեթե ցանկացած ժամանակակից հավելված դա անում է:
  • Բեռնում է ասինխրոն տվյալներ հարցումների վրա և վերլուծում JSON-ը Swift մոդելում՝ օգտագործելով Կոդավորելի.
  • Ցույց է տալիս խնդրանքով բեռնված պատկերները և պահում դրանք:
  • Այս հավելվածը iOS-ի, iPadOS-ի և macOS-ի համար ապահովում է լավագույն UX-ը այս ՕՀ-երի օգտատերերի համար:
  • Օգտագործողը կարող է տվյալներ ստեղծել և ստեղծել իր սեփական ֆիլմերի ցուցակները: Հավելվածը պահպանում և վերականգնում է օգտվողի տվյալները:
  • Դիտումները, բաղադրիչները և մոդելները հստակորեն առանձնացված են Redux-ի օրինակով: Տվյալների հոսքն այստեղ միակողմանի է: Այն կարող է ամբողջությամբ պահվել, վերականգնվել և վերագրվել:
  • Հավելվածն օգտագործում է SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal և այլն հիմնական բաղադրիչները: Այն նաև տրամադրում է անհատական ​​դիտումներ, ժեստեր, UI/UX:

Հավելվածի մշակում SwiftUI-ում: Մաս 1. Dataflow և Redux
Իրականում, անիմացիան հարթ է, GIF-ը մի փոքր ցնցող է ստացվել

Հավելվածի վրա աշխատելն ինձ մեծ փորձ տվեց, և ընդհանուր առմամբ դա դրական փորձ էր: Ես կարողացա գրել լիարժեք ֆունկցիոնալ հավելված, սեպտեմբերին այն կբարելավեմ և կհրապարակեմ AppStore-ում՝ iOS 13-ի թողարկման հետ միաժամանակ։

Redux, BindableObject և EnvironmentObject

Հավելվածի մշակում SwiftUI-ում: Մաս 1. Dataflow և Redux

Ես աշխատում եմ Redux-ի հետ արդեն մոտ երկու տարի, ուստի համեմատաբար լավ եմ տիրապետում դրան: Մասնավորապես, ես այն օգտագործում եմ ճակատային մասում Արձագանքել կայք, ինչպես նաև տեղական iOS (Swift) և Android (Kotlin) հավելվածներ մշակելու համար։

Ես երբեք չեմ զղջացել Redux-ի ընտրության համար որպես տվյալների հոսքի ճարտարապետություն SwiftUI հավելված ստեղծելու համար: UIKit հավելվածում Redux-ն օգտագործելիս ամենադժվար մասերը խանութի հետ աշխատելն են և տվյալների ստացումն ու առբերումը և դրանց քարտեզագրումը ձեր դիտումների/բաղադրիչների հետ: Դա անելու համար ես պետք է ստեղծեի միակցիչների մի տեսակ գրադարան (օգտագործելով ReSwift և ReKotlin): Լավ է աշխատում, բայց բավականին շատ կոդ: Ցավոք, այն (դեռ) բաց կոդով չէ։

Լավ լուր! SwiftUI-ի հետ անհանգստանալու միակ բանը, եթե նախատեսում եք օգտագործել Redux-ը, խանութներն են, նահանգները և կրճատիչները: Խանութի հետ փոխգործակցությունն ամբողջությամբ հոգում է SwiftUI-ը՝ @EnvironmentObject-ի շնորհիվ: Այսպիսով, խանութը սկսվում է BindableObject-ից:

Ես ստեղծել եմ պարզ Swift փաթեթ, SwiftUIFlux, որն ապահովում է Redux-ի հիմնական օգտագործումը: Իմ դեպքում դա MovieSwiftUI-ի մի մասն է: ես նույնպես գրել է քայլ առ քայլ ձեռնարկ, որը կօգնի ձեզ օգտագործել այս բաղադրիչը:

Ինչպես է դա աշխատում.

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

Ամեն անգամ, երբ դուք գործարկում եք որևէ գործողություն, դուք ակտիվացնում եք փոխանցումատուփը: Այն կգնահատի գործողությունները՝ համաձայն հավելվածի ներկա վիճակի: Այնուհետև այն կվերադարձնի նոր փոփոխված վիճակ՝ գործողության տեսակին և տվյալներին համապատասխան:

Դե, քանի որ խանութը BindableObject է, այն կտեղեկացնի SwiftUI-ին, երբ դրա արժեքը փոխվի՝ օգտագործելով PassthroughSubject-ի կողմից տրամադրված willChange հատկությունը: Դա պայմանավորված է նրանով, որ BindableObject-ը պետք է տրամադրի PublisherType, սակայն արձանագրության իրականացումը պատասխանատու է այն կառավարելու համար: Ընդհանուր առմամբ, սա Apple-ի շատ հզոր գործիք է: Համապատասխանաբար, հաջորդ արտապատկերման ցիկլում SwiftUI-ն կօգնի ցուցադրել դիտումների ամբողջությունը՝ ըստ վիճակի փոփոխության:

Փաստորեն, սա է SwiftUI-ի ողջ սիրտն ու կախարդանքը: Այժմ, ցանկացած դիտում, որը բաժանորդագրվում է պետությանը, դիտումը կարտացվի ըստ այն մասին, թե ինչ տվյալներ են ստացվել պետությունից և ինչ են փոխվել:

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

Խանութը ներարկվում է որպես EnvironmentObject, երբ հավելվածը մեկնարկում է, և այնուհետև հասանելի է ցանկացած տեսքով՝ օգտագործելով @EnvironmentObject: Չկա կատարման տույժ, քանի որ ստացված հատկությունները արագ վերականգնվում կամ հաշվարկվում են կիրառման վիճակից:

Վերևի կոդը փոխում է պատկերը, եթե ֆիլմի պաստառը փոխվի:

Իսկ դա իրականում արվում է ընդամենը մեկ տողով, որի օգնությամբ տեսակետները կապվում են պետության հետ։ Եթե ​​դուք աշխատել եք ReSwift-ի հետ iOS-ով կամ նույնիսկ միացնել React-ի հետ դուք կհասկանաք SwiftUI-ի կախարդանքը:

Այժմ կարող եք փորձել ակտիվացնել գործողությունը և հրապարակել նոր վիճակը: Ահա ավելի բարդ օրինակ.

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

Վերևի կոդում ես օգտագործում եմ .onDelete գործողությունը SwiftUI-ից յուրաքանչյուր IP-ի համար: Սա թույլ է տալիս ցանկի տողին ցուցադրել ջնջման համար նախատեսված iOS-ի սովորական սահեցումը: Այսպիսով, երբ օգտվողը դիպչում է ջնջման կոճակին, այն գործարկում է համապատասխան գործողությունը և հեռացնում ֆիլմը ցանկից:

Դե, քանի որ ցուցակի հատկությունը բխում է BindableObject վիճակից և ներարկվում է որպես EnvironmentObject, SwiftUI-ն թարմացնում է ցուցակը, քանի որ ForEach-ը կապված է ֆիլմերի հաշվարկված հատկության հետ:

Ահա 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
}

Կրճատիչը գործարկվում է, երբ դուք կատարում եք գործողություն և վերադարձնում եք նոր վիճակ, ինչպես նշված է վերևում:

Ես դեռ չեմ մանրամասնի. ինչպես SwiftUI-ն իրականում գիտի, թե ինչ ցուցադրել: Սա ավելի խորը հասկանալու համար արժե դիտեք WWDC նիստը տվյալների հոսքի վերաբերյալ SwiftUI-ում: Այն նաև մանրամասնորեն բացատրում է, թե ինչու և երբ օգտագործել պետական, @Binding, ObjectBinding և EnvironmentObject:

Skillbox-ը խորհուրդ է տալիս.

Source: www.habr.com

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