SwiftUIде тиркемени иштеп чыгуу. 1-бөлүк: Dataflow жана Redux

SwiftUIде тиркемени иштеп чыгуу. 1-бөлүк: Dataflow жана Redux

WWDC 2019 Союздун абалы сессиясына катышкандан кийин, мен SwiftUIге терең сүңгүүнү чечтим. Мен аны менен иштөө үчүн көп убакыт өткөрдүм жана азыр колдонуучулардын кеңири чөйрөсүнө пайдалуу боло турган реалдуу тиркемени иштеп чыгууну баштадым.

Мен аны MovieSwiftUI деп атадым - бул жаңы жана эски тасмаларды издөө, ошондой эле аларды коллекцияга чогултуу үчүн колдонмо TMDB API. Мен ар дайым тасмаларды жакшы көрчүмүн, ал тургай, көп убакыт мурун болсо да, бул тармакта иштеген компанияны түздүм. Компанияны салкын деп айтуу кыйын, бирок колдонмо!

Биз эсиңизге салабыз: "Хабрдын" бардык окурмандары үчүн - "Habr" промо-кодун колдонуу менен каалаган Skillbox курсуна катталганда 10 000 рубль арзандатуу.

Skillbox сунуштайт: Онлайн билим берүү курсу "Жава иштеп чыгуучу кесиби".

Ошентип, MovieSwiftUI эмне кыла алат?

  • API менен өз ара аракеттенет - дээрлик бардык заманбап тиркемелер муну жасайт.
  • Сурамдар боюнча асинхрондук маалыматтарды жүктөйт жана колдонуу менен JSONди Swift моделине талдайт Codeable.
  • Сурам боюнча жүктөлгөн сүрөттөрдү көрсөтөт жана аларды кэштейт.
  • iOS, iPadOS жана macOS үчүн бул колдонмо ушул ОСтин колдонуучулары үчүн мыкты UX менен камсыз кылат.
  • Колдонуучу маалыматтарды жаратып, өз тасма тизмелерин түзө алат. Колдонмо колдонуучунун маалыматтарын сактайт жана калыбына келтирет.
  • Көрүнүштөр, компоненттер жана моделдер Redux үлгүсү аркылуу так бөлүнгөн. Бул жерде маалымат агымы бир багыттуу болуп саналат. Аны толугу менен кэштеп, калыбына келтирип, үстүнө жазса болот.
  • Колдонмо SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal ж.б. негизги компоненттерин колдонот. Ал ошондой эле ыңгайлаштырылган көрүнүштөрдү, жаңсоолорду, UI/UX менен камсыз кылат.

SwiftUIде тиркемени иштеп чыгуу. 1-бөлүк: Dataflow жана Redux
Чынында, анимация жылмакай, GIF бир аз тырмак болуп чыкты

Колдонмонун үстүндө иштөө мага көп тажрыйба берди жана жалпысынан бул оң тажрыйба болду. Мен толук функционалдык тиркемени жаза алдым, сентябрда мен аны өркүндөтөм жана iOS 13 чыгарылышы менен бир убакта AppStoreда жарыялайм.

Redux, BindableObject жана EnvironmentObject

SwiftUIде тиркемени иштеп чыгуу. 1-бөлүк: Dataflow жана Redux

Мен Redux менен эки жылдан бери иштеп жатам, ошондуктан мен аны салыштырмалуу жакшы билем. Атап айтканда, мен аны for frontend үчүн колдоном иш-аракет кылгыла веб-сайты, ошондой эле жергиликтүү iOS (Swift) жана Android (Kotlin) тиркемелерин иштеп чыгуу үчүн.

SwiftUI тиркемесин куруу үчүн маалымат агымынын архитектурасы катары Reduxти тандаганыма эч качан өкүнгөн эмесмин. UIKit колдонмосунда Reduxти колдонууда эң татаал бөлүктөрү дүкөн менен иштөө жана маалыматтарды алуу жана алуу жана аны көз караштарыңызга/компоненттериңизге түшүрүү. Бул үчүн, мен туташтыргычтардын китепкана түрүн түзүшүм керек болчу (ReSwift жана ReKotlin аркылуу). Жакшы иштейт, бирок коду абдан көп. Тилекке каршы, ал (азырынча) ачык булак эмес.

Жакшы Жаңылыктар! SwiftUI менен тынчсыздана турган жалгыз нерселер - эгер сиз Redux'ту колдонууну пландаштырсаңыз - бул дүкөндөр, штаттар жана редукторлор. Дүкөн менен өз ара аракеттенүү толугу менен @EnvironmentObject аркасында SwiftUI тарабынан колго алынат. Ошентип, дүкөн 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 болгондуктан, PassthroughSubject тарабынан берилген willChange касиети аркылуу анын мааниси өзгөргөндө SwiftUIге кабарлайт. Себеби, 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!]))
            }
        }
    }
}

Жогорудагы коддо мен ар бир IP үчүн SwiftUIден .onDelete аракетин колдонуп жатам. Бул тизмедеги катарды жок кылуу үчүн кадимки 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

Комментарий кошуу