Развој апликација на СвифтУИ. Део 1: Ток података и Редук

Развој апликација на СвифтУИ. Део 1: Ток података и Редук

Након што сам присуствовао седници о стању у Унији на ВВДЦ 2019, одлучио сам да дубоко зароним у СвифтУИ. Провео сам доста времена радећи са њим и сада сам почео да развијам праву апликацију која може бити корисна широком спектру корисника.

Назвао сам га МовиеСвифтУИ - ово је апликација за тражење нових и старих филмова, као и њихово прикупљање у колекцију помоћу ТМДБ АПИ. Увек сам волео филмове и чак сам основао компанију која ради у овој области, мада давно. Компанија се тешко може назвати цоол, али апликација јесте!

Подсећамо: за све читаоце „Хабра“ – попуст од 10 рубаља при упису на било који курс Скиллбок користећи промотивни код „Хабр“.

Скиллбок препоручује: Образовни онлајн курс „Професија Јава програмер“.

Дакле, шта може МовиеСвифтУИ?

  • Интерагује са АПИ-јем - скоро свака модерна апликација то ради.
  • Учитава асинхроне податке о захтевима и анализира ЈСОН у Свифт модел користећи Цодабле.
  • Приказује слике учитане на захтев и кешира их.
  • Ова апликација је за iOS, iPadOS и macOS пружа најбоље корисничко искуство за кориснике ових оперативних система.
  • Корисник може да генерише податке и креира сопствене листе филмова. Апликација чува и враћа корисничке податке.
  • Погледи, компоненте и модели су јасно раздвојени помоћу Редук шаблона. Проток података овде је једносмеран. Може се у потпуности кеширати, вратити и преписати.
  • Апликација користи основне компоненте СвифтУИ, ТаббедВиев, СегментедЦонтрол, НавигатионВиев, Форм, Модал, итд. Такође пружа прилагођене приказе, покрете, УИ/УКС.

Развој апликација на СвифтУИ. Део 1: Ток података и Редук
У ствари, анимација је глатка, ГИФ је испао мало трзав

Рад на апликацији дао ми је пуно искуства и свеукупно је било позитивно искуство. Успео сам да напишем потпуно функционалну апликацију, у септембру ћу је побољшати и објавити у АппСторе-у, истовремено са издавањем иОС 13.

Редук, БиндаблеОбјецт и ЕнвиронментОбјецт

Развој апликација на СвифтУИ. Део 1: Ток података и Редук

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

Никада нисам пожалио што сам изабрао Редук као архитектуру протока података за изградњу СвифтУИ апликације. Најизазовнији делови при коришћењу Редук-а у апликацији УИКит су рад са продавницом и добијање и преузимање података и њихово мапирање у ваше погледе/компоненте. Да бих то урадио, морао сам да направим неку врсту библиотеке конектора (користећи РеСвифт и РеКотлин). Ради добро, али доста кода. Нажалост, (још) није отвореног кода.

Добре вести! Једине ствари о којима треба да бринете са СвифтУИ - ако планирате да користите Редук - су продавнице, стања и редуктори. СвифтУИ у потпуности брине о интеракцији са продавницом захваљујући @ЕнвиронментОбјецт. Дакле, продавница почиње са БиндаблеОбјецт.

Направио сам једноставан Свифт пакет, СвифтУИФлук, који обезбеђује основну употребу Редук-а. У мом случају то је део МовиеСвифтУИ. Такође написао корак по корак туторијал, што ће вам помоћи да користите ову компоненту.

Како то функционише?

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

Сваки пут када покренете неку акцију, активирате мењач. Он ће проценити акције према тренутном стању апликације. Затим ће вратити ново измењено стање у складу са типом акције и подацима.

Па, пошто је продавница БиндаблеОбјецт, она ће обавестити СвифтУИ када се његова вредност промени помоћу својства виллЦханге које обезбеђује ПасстхроугхСубјецт. То је зато што БиндаблеОбјецт мора да обезбеди ПублисхерТипе, али имплементација протокола је одговорна за управљање њиме. Све у свему, ово је веома моћан алат компаније Аппле. Сходно томе, у следећем циклусу рендеровања, СвифтУИ ће помоћи у приказивању тела погледа у складу са променом стања.

Заправо, ово је све срце и магија СвифтУИ-ја. Сада, у сваком погледу који се претплати на стање, приказ ће бити приказан у складу са подацима који су примљени од стања и шта се променило.

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

Продавница се убацује као ЕнвиронментОбјецт када се апликација покрене, а затим је доступна у било ком приказу помоћу @ЕнвиронментОбјецт. Не постоји казна за перформансе јер се изведена својства брзо преузимају или израчунавају из стања апликације.

Код изнад мења слику ако се промени постер филма.

А то се заправо ради само једном линијом, уз помоћ које се погледи повезују са државом. Ако сте радили са РеСвифт-ом на иОС-у или чак повезати са Реацт-ом, разумећете магију СвифтУИ.

Сада можете покушати да активирате акцију и објавите ново стање. Ево сложенијег примера.

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

У коду изнад, користим акцију .онДелете из СвифтУИ за сваку ИП адресу. Ово омогућава да ред на листи приказује нормалан иОС превлачење за брисање. Дакле, када корисник додирне дугме за брисање, он покреће одговарајућу радњу и уклања филм са листе.

Па, пошто је својство листе изведено из стања БиндаблеОбјецт и убризгано је као ЕнвиронментОбјецт, СвифтУИ ажурира листу јер је ФорЕацх повезан са израчунатим својством филмова.

Ево дела редуктора МовиесСтате:

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
}

Редуктор се извршава када пошаљете акцију и вратите ново стање, као што је горе наведено.

Нећу још улазити у детаље – како СвифтУИ заправо зна шта да прикаже. Да бисмо ово дубље разумели, вреди погледајте ВВДЦ сесију о протоку података у СвифтУИ. Такође детаљно објашњава зашто и када користити Држава, @Биндинг, ОбјецтБиндинг и ЕнвиронментОбјецт.

Скиллбок препоручује:

Извор: ввв.хабр.цом

Купите поуздан хостинг за сајтове са ДДоС заштитом, ВПС ВДС сервере 🔥 Купите поуздан веб хостинг са DDoS заштитом, VPS VDS сервере | ProHoster