SwiftUI-də tətbiqin inkişafı. 1-ci hissə: Dataflow və Redux

SwiftUI-də tətbiqin inkişafı. 1-ci hissə: Dataflow və Redux

WWDC 2019-da Birliyin Vəziyyəti sessiyasında iştirak etdikdən sonra SwiftUI-yə dərindən girməyə qərar verdim. Mən onunla işləmək üçün çox vaxt sərf etdim və indi geniş istifadəçilər üçün faydalı ola biləcək real proqram hazırlamağa başladım.

Mən onu MovieSwiftUI adlandırdım - bu, yeni və köhnə filmləri axtarmaq, həmçinin onları kolleksiyada toplamaq üçün proqramdır. TMDB API. Mən həmişə filmləri sevmişəm və hətta uzun müddət əvvəl də olsa, bu sahədə çalışan şirkət yaratmışam. Şirkəti çətin ki, sərin adlandırmaq olar, amma tətbiq belə idi!

Xatırladırıq: "Habr" ın bütün oxucuları üçün - "Habr" promosyon kodundan istifadə edərək hər hansı bir Skillbox kursuna yazılarkən 10 000 rubl endirim.

Skillbox tövsiyə edir: Onlayn təhsil kursu "Peşə Java Developer".

Beləliklə, MovieSwiftUI nə edə bilər?

  • API ilə qarşılıqlı əlaqə yaradır - demək olar ki, hər hansı bir müasir proqram bunu edir.
  • Sorğular üzrə asinxron məlumatları yükləyir və istifadə edərək JSON-u Swift modelinə təhlil edir Kodlaşdırıla bilən.
  • İstək üzrə yüklənmiş şəkilləri göstərir və onları keşləyir.
  • iOS, iPadOS və macOS üçün bu proqram bu əməliyyat sistemlərinin istifadəçiləri üçün ən yaxşı UX təmin edir.
  • İstifadəçi məlumat yarada və öz film siyahılarını yarada bilər. Tətbiq istifadəçi məlumatlarını saxlayır və bərpa edir.
  • Görünüşlər, komponentlər və modellər Redux nümunəsi ilə aydın şəkildə ayrılır. Burada məlumat axını bir istiqamətlidir. O, tam yaddaşda saxlanıla, bərpa oluna və üzərinə yazıla bilər.
  • Tətbiq SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal və s.-nin əsas komponentlərindən istifadə edir. O, həmçinin fərdi görünüşlər, jestlər, UI/UX təmin edir.

SwiftUI-də tətbiqin inkişafı. 1-ci hissə: Dataflow və Redux
Əslində, animasiya hamardır, GIF bir az əyri çıxdı

Proqram üzərində işləmək mənə çoxlu təcrübə qazandırdı və ümumilikdə müsbət təcrübə oldu. Mən tam funksional proqram yaza bildim, sentyabr ayında onu təkmilləşdirəcəyəm və iOS 13-ün buraxılışı ilə eyni vaxtda AppStore-da dərc edəcəyəm.

Redux, BindableObject və EnvironmentObject

SwiftUI-də tətbiqin inkişafı. 1-ci hissə: Dataflow və Redux

Təxminən iki ildir ki, Redux ilə işləyirəm, ona görə də onu nisbətən yaxşı bilirəm. Xüsusilə, mən onu frontend üçün istifadə edirəm Reaksiya veb-sayt, həmçinin yerli iOS (Swift) və Android (Kotlin) proqramlarının hazırlanması üçün.

SwiftUI tətbiqini qurmaq üçün məlumat axını arxitekturası kimi Redux-u seçdiyimə heç vaxt peşman olmamışam. UIKit proqramında Redux istifadə edərkən ən çətin hissələr mağaza ilə işləmək və məlumatların əldə edilməsi və əldə edilməsi və onların baxışlarınıza/komponentlərinizə uyğunlaşdırılmasıdır. Bunun üçün mən bir növ bağlayıcı kitabxana yaratmalı oldum (ReSwift və ReKotlin istifadə edərək). Yaxşı işləyir, lakin kifayət qədər çox kod var. Təəssüf ki, o (hələ) açıq mənbə deyil.

Yaxşı xəbər! SwiftUI ilə narahat olmaq üçün yeganə şey - əgər Redux-dan istifadə etməyi planlaşdırırsınızsa - mağazalar, dövlətlər və reduktorlardır. Mağaza ilə qarşılıqlı əlaqə @EnvironmentObject sayəsində SwiftUI tərəfindən tamamilə həll edilir. Beləliklə, mağaza BindableObject ilə başlayır.

Sadə bir Swift paketi yaratdım, SwiftUIFluxRedux-un əsas istifadəsini təmin edən. Mənim vəziyyətimdə bu, MovieSwiftUI-nin bir hissəsidir. mən də addım-addım təlimat yazdı, bu komponentdən istifadə etməyə kömək edəcək.

Necə işləyir?

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

Hər hansı bir hərəkəti işə saldıqda, sürət qutusunu işə salırsınız. O, hərəkətləri tətbiqin cari vəziyyətinə görə qiymətləndirəcək. Daha sonra fəaliyyət növü və məlumatlara uyğun olaraq yeni dəyişdirilmiş vəziyyəti qaytaracaq.

Mağaza BindableObject olduğundan, PassthroughSubject tərəfindən təmin edilən willChange xassəsindən istifadə edərək dəyəri dəyişdikdə SwiftUI-yə məlumat verəcək. Bunun səbəbi, BindableObject-in PublisherType təmin etməli olmasıdır, lakin protokolun icrası onun idarə edilməsinə cavabdehdir. Ümumiyyətlə, bu Apple-dan çox güclü bir vasitədir. Müvafiq olaraq, növbəti renderinq dövründə SwiftUI vəziyyət dəyişikliyinə uyğun olaraq baxışların əsas hissəsini göstərməyə kömək edəcək.

Əslində, bu, SwiftUI-nin bütün ürəyi və sehridir. İndi dövlətə abunə olan hər hansı görünüşdə dövlətdən hansı məlumatların alındığına və nəyin dəyişdiyinə görə görünüş göstəriləcək.

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

Tətbiq başlayanda Mağaza EnvironmentObject kimi daxil edilir və sonra @EnvironmentObject istifadə edərək istənilən görünüşdə əlçatan olur. Əldə edilmiş xüsusiyyətlər tətbiq vəziyyətindən tez əldə edildiyi və ya hesablandığı üçün heç bir performans cəzası yoxdur.

Filmin posteri dəyişdikdə yuxarıdakı kod şəkli dəyişir.

Və bu, əslində yalnız bir xəttlə həyata keçirilir, onun köməyi ilə baxışlar dövlətə bağlıdır. Əgər siz iOS və ya hətta ReSwift ilə işləmisinizsə birləşdirmək React ilə siz SwiftUI-nin sehrini başa düşəcəksiniz.

İndi siz hərəkəti aktivləşdirib yeni vəziyyəti dərc etməyə cəhd edə bilərsiniz. Burada daha mürəkkəb bir nümunə var.

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

Yuxarıdakı kodda mən hər bir IP üçün SwiftUI-dən .onDelete əməliyyatından istifadə edirəm. Bu, siyahıdakı sıraya silmək üçün adi iOS sürüşməsini göstərməyə imkan verir. Beləliklə, istifadəçi sil düyməsinə toxunduqda, müvafiq hərəkəti işə salır və filmi siyahıdan çıxarır.

Yaxşı, siyahı xassəsi BindableObject vəziyyətindən əldə edildiyi və EnvironmentObject kimi yeridildiyi üçün SwiftUI siyahını yeniləyir, çünki ForEach filmlərin hesablanmış xüsusiyyəti ilə əlaqələndirilir.

Budur MoviesState reduktorunun bir hissəsi:

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, yuxarıda qeyd edildiyi kimi, bir hərəkət göndərdiyiniz və yeni vəziyyətə qayıtdığınız zaman yerinə yetirilir.

Hələ təfərrüata varmayacağam - SwiftUI əslində nəyi göstərəcəyini necə bilir. Bunu daha dərindən başa düşmək üçün buna dəyər məlumat axınında WWDC sessiyasına baxın SwiftUI-də. Nə üçün və nə vaxt istifadə olunacağını da ətraflı izah edir dövlət, @Binding, ObjectBinding və EnvironmentObject.

Skillbox tövsiyə edir:

Mənbə: www.habr.com

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