Развој на апликации на SwiftUI. Дел 1: Проток на податоци и Редукс

Развој на апликации на SwiftUI. Дел 1: Проток на податоци и Редукс

Откако присуствував на сесијата за состојбата на Унијата на WWDC 2019, решив длабоко да се нурне во SwiftUI. Поминав многу време работејќи со него и сега почнав да развивам вистинска апликација која може да биде корисна за широк опсег на корисници.

Ја нареков MovieSwiftUI - ова е апликација за пребарување на нови и стари филмови, како и нивно собирање во колекција користејќи TMDB API. Отсекогаш сум сакал филмови, па дури и создадов компанија која работи на ова поле, иако многу одамна. Компанијата тешко може да се нарече кул, но апликацијата беше!

Потсетуваме: за сите читатели на „Хабр“ - попуст од 10 рубли при запишување на кој било курс Skillbox користејќи го промотивниот код „Хабр“.

Skillbox препорачува: Едукативен онлајн курс „Професија Java програмер“.

Значи, што може да направи MovieSwiftUI?

  • Се поврзува со API - речиси секоја модерна апликација го прави ова.
  • Вчитува асинхрони податоци на барања и го анализира JSON во моделот Swift користејќи Кодливи.
  • Ги прикажува сликите вчитани по барање и ги кешира.
  • Оваа апликација за iOS, iPadOS и macOS го обезбедува најдобриот UX за корисниците на овие ОС.
  • Корисникот може да генерира податоци и да креира свои списоци со филмови. Апликацијата зачувува и обновува кориснички податоци.
  • Прегледите, компонентите и моделите се јасно одвоени со користење на моделот Redux. Протокот на податоци овде е еднонасочен. Може да биде целосно кеширан, обновен и препишан.
  • Апликацијата ги користи основните компоненти на SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal итн. Исто така, обезбедува прилагодени прикази, гестови, UI/UX.

Развој на апликации на SwiftUI. Дел 1: Проток на податоци и Редукс
Всушност, анимацијата е мазна, GIF-от испадна малку отсечен

Работењето на апликацијата ми даде многу искуство и генерално беше позитивно искуство. Можев да напишам целосно функционална апликација, во септември ќе ја подобрам и ќе ја објавам во AppStore, истовремено со објавувањето на iOS 13.

Redux, BindableObject и EnvironmentObject

Развој на апликации на SwiftUI. Дел 1: Проток на податоци и Редукс

Работам со Redux веќе околу две години, така што сум релативно добро упатен во тоа. Конкретно, го користам во предниот дел за Реагираат веб-страница, како и за развој на домашни апликации за iOS (Swift) и Android (Kotlin).

Никогаш не се покајав што го избрав Redux како архитектура на проток на податоци за изградба на апликација SwiftUI. Најпредизвикувачките делови при користење на Redux во апликацијата UIKit се работа со продавницата и добивање и преземање податоци и нивно мапирање на вашите погледи/компоненти. За да го направам ова, морав да создадам еден вид библиотека на конектори (користејќи 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 кога неговата вредност ќе се промени со користење на својството willChange обезбедено од PassthroughSubject. Ова е затоа што 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 препорачува:

Извор: www.habr.com

Додадете коментар