
Setelah berpartisipasi dalam sesi State of the Union di WWDC 2019, saya memutuskan untuk mempelajari SwiftUI secara terperinci. Saya menghabiskan banyak waktu bekerja dengannya dan sekarang saya mulai mengembangkan aplikasi nyata yang dapat bermanfaat bagi berbagai pengguna.
Saya menyebutnya MovieSwiftUI - ini adalah aplikasi untuk mencari film baru dan lama, serta mengumpulkannya menjadi koleksi menggunakan
TMDB API . Saya selalu menyukai film dan bahkan menciptakan perusahaan yang bergerak di bidang ini, meskipun untuk waktu yang lama. Sulit untuk menyebut perusahaan itu keren, tetapi aplikasinya - ya!
Kami mengingatkan Anda: untuk semua pembaca "Habr" - diskon 10.000 rubel saat mendaftar untuk kursus Skillbox apa pun menggunakan kode promo "Habr".
Skillbox merekomendasikan: Kursus pendidikan online "Pengembang Java Profesi" .
Jadi apa yang dilakukan MovieSwiftUI?
- Berinteraksi dengan API - inilah yang hampir semua aplikasi modern lakukan.
- Memuat data permintaan asinkron dan mem-parsing JSON dalam model Swift menggunakan Codable .
- Menampilkan gambar yang diunduh sesuai permintaan dan menyimpannya.
- Aplikasi ini untuk iOS, iPadOS, dan macOS menyediakan UX terbaik untuk pengguna OS ini.
- Pengguna dapat menghasilkan data, membuat daftar film mereka sendiri. Aplikasi menyimpan dan mengembalikan data pengguna.
- Tampilan, komponen, dan model dipisahkan dengan jelas menggunakan pola Redux. Aliran data searah di sini. Itu bisa sepenuhnya di-cache, dipulihkan, dan ditimpa.
- Aplikasi ini menggunakan komponen dasar SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal, dll. Ini juga menyediakan tampilan kustom, gerakan, UI / UX.
Bahkan, animasinya lancar, GIF ternyata sedikit gugupBekerja pada aplikasi memberi saya banyak pengalaman, dan secara keseluruhan itu adalah pengalaman positif. Saya bisa menulis aplikasi yang berfungsi penuh, pada bulan September saya akan memperbaikinya dan meletakkannya di AppStore, bersamaan dengan rilis iOS 13.
Redux, BindableObject, dan EnvironmentObject

Saya telah bekerja dengan Redux selama sekitar dua tahun sekarang, jadi saya tahu ini dengan relatif baik. Secara khusus, saya menggunakannya di frontend untuk situs web
Bereaksi , serta untuk mengembangkan aplikasi iOS asli (Swift) dan Android (Kotlin).
Saya tidak pernah menyesal memilih Redux sebagai arsitektur aliran data untuk membangun aplikasi di SwiftUI. Saat-saat paling sulit ketika menggunakan Redux dalam aplikasi UIKit adalah bekerja dengan toko, serta mendapatkan dan mengambil data dan membandingkannya dengan pandangan / komponen Anda. Untuk melakukan ini, saya harus membuat semacam perpustakaan konektor (di ReSwift dan ReKotlin). Bekerja dengan baik, tetapi cukup banyak kode. Sayangnya, ini (belum) open source.
Kabar baik! Satu-satunya hal yang perlu dikhawatirkan dengan SwiftUI - jika Anda berencana menggunakan Redux - adalah store, state, dan reducers. SwiftUI mengambil alih interaksi dengan toko berkat @EnvironmentObject. Jadi, toko dimulai dengan BindableObject.
Saya membuat paket Swift sederhana,
SwiftUIFlux , yang menyediakan penggunaan dasar Redux. Dalam kasus saya, ini adalah bagian dari MovieSwiftUI. Saya juga
menulis tutorial langkah demi langkah untuk membantu Anda menggunakan komponen ini.
Bagaimana cara kerjanya?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) } }
Setiap kali Anda memulai suatu tindakan, Anda mengaktifkan gearbox. Dia akan mengevaluasi tindakan sesuai dengan keadaan aplikasi saat ini. Kemudian akan mengembalikan negara yang dimodifikasi baru sesuai dengan jenis tindakan dan data.
Nah, karena store adalah BindableObject, ia akan memberi tahu SwiftUI tentang perubahan nilainya menggunakan properti willChange yang disediakan oleh PassthroughSubject. Ini karena BindableObject harus menyediakan PublisherType, tetapi implementasi protokol bertanggung jawab untuk mengelolanya. Secara keseluruhan, ini adalah alat yang sangat kuat dari Apple. Karenanya, dalam siklus rendering berikutnya, SwiftUI akan membantu menampilkan isi representasi sesuai dengan perubahan status.
Sebenarnya, ini semua - jantung dan keajaiban SwiftUI. Sekarang, dalam tampilan apa pun yang berlangganan negara, tampilan akan ditampilkan sesuai dengan data apa yang diterima dari negara dan apa yang telah berubah.
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()) } }
Store diimplementasikan sebagai EnvironmentObject ketika aplikasi dimulai, dan kemudian tersedia dalam tampilan apa pun menggunakan @EnvironmentObject. Kinerja tidak berkurang karena properti yang diturunkan dengan cepat diambil atau dihitung dari kondisi aplikasi.
Kode di atas mengubah gambar jika poster untuk film berubah.
Dan ini sebenarnya dilakukan hanya dalam satu baris, dengan bantuan yang pandangannya terhubung ke negara. Jika Anda bekerja dengan ReSwift di iOS atau bahkan
terhubung dengan React, Anda akan mengerti apa itu sihir SwiftUI.
Dan sekarang Anda dapat mencoba mengaktifkan tindakan dan menerbitkan negara baru. Ini adalah contoh yang lebih kompleks.
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!])) } } } }
Dalam kode di atas, saya menggunakan tindakan .onDelete dari SwiftUI untuk setiap IP. Ini memungkinkan baris dalam daftar untuk menampilkan usapan iOS yang biasa dihapus. Oleh karena itu, ketika pengguna menyentuh tombol hapus, ia memulai tindakan yang sesuai dan menghapus film dari daftar.
Nah, karena properti daftar diturunkan dari keadaan BindableObject dan diimplementasikan sebagai EnvironmentObject, SwiftUI memperbarui daftar, karena ForEach dikaitkan dengan properti film yang dihitung.
Ini adalah bagian dari peredam MoviesState:
func moviesStateReducer(state: MoviesState, action: Action) -> MoviesState { var state = state switch action {
Peredam dijalankan ketika Anda mengirimkan tindakan dan mengembalikan negara baru, seperti yang disebutkan di atas.
Saya tidak akan membahas detail untuk saat ini - bagaimana SwiftUI benar-benar tahu apa yang harus ditampilkan. Untuk memahami ini lebih dalam, Anda harus
melihat sesi WWDC tentang aliran data di SwiftUI. Ini juga menjelaskan secara terperinci mengapa dan kapan menggunakan
State , @Binding, ObjectBinding, dan EnvironmentObject.
Skillbox merekomendasikan: