
WWDC 2019'daki Birliğin Durumu oturumuna katıldıktan sonra SwiftUI'yı derinlemesine incelemeye karar verdim. Üzerinde çalışmak için çok zaman harcadım ve şimdi geniş bir kullanıcı kitlesine faydalı olabilecek gerçek bir uygulama geliştirmeye başladım.
Buna MovieSwiftUI adını verdim - bu, yeni ve eski filmleri aramanın yanı sıra bunları kullanarak bir koleksiyonda toplamaya yönelik bir uygulamadır. . Filmleri her zaman çok sevdim ve hatta uzun zaman önce de olsa bu alanda çalışan bir şirket kurdum. Şirketin havalı olduğu söylenemezdi ama uygulama öyleydi!
Hatırlatıyoruz: tüm "Habr" okuyucuları için - "Habr" promosyon kodunu kullanarak herhangi bir Skillbox kursuna kayıt olurken 10 ruble indirim.
Skillbox şunları önerir: Eğitici çevrimiçi kurs .
Peki MovieSwiftUI ne yapabilir?
- API ile etkileşime girer; hemen hemen her modern uygulama bunu yapar.
- İsteklere eşzamansız verileri yükler ve JSON'u kullanarak Swift modeline ayrıştırır. .
- İstek üzerine yüklenen görüntüleri gösterir ve bunları önbelleğe alır.
- Bu uygulama iOS, iPadOS ve diğer işletim sistemleri için tasarlanmıştır. macOS Bu işletim sistemlerinin kullanıcıları için en iyi kullanıcı deneyimini sağlar.
- Kullanıcı veri üretebilir ve kendi film listelerini oluşturabilir. Uygulama kullanıcı verilerini kaydeder ve geri yükler.
- Görünümler, bileşenler ve modeller Redux modeli kullanılarak net bir şekilde ayrılmıştır. Buradaki veri akışı tek yönlüdür. Tamamen önbelleğe alınabilir, geri yüklenebilir ve üzerine yazılabilir.
- Uygulama SwiftUI, TabbedView, SegmentedControl, NavigasyonView, Form, Modal vb. temel bileşenlerini kullanır. Ayrıca özel görünümler, hareketler ve UI/UX sağlar.

Aslında animasyon düzgün, GIF biraz sarsıntılı çıktı
Uygulama üzerinde çalışmak bana çok fazla deneyim kazandırdı ve genel olarak olumlu bir deneyimdi. Tamamen işlevsel bir uygulama yazmayı başardım, Eylül ayında onu geliştirip iOS 13'ün çıkışıyla eş zamanlı olarak AppStore'da yayınlayacağım.
Redux, BindableObject ve EnvironmentObject

Yaklaşık iki yıldır Redux ile çalışıyorum, dolayısıyla bu konuda oldukça bilgiliyim. Özellikle ön uçta kullanıyorum web sitesi geliştirmek ve ayrıca yerel iOS (Swift) uygulamaları geliştirmek için. Android (Kotlin) uygulamaları.
SwiftUI uygulaması oluşturmak için veri akışı mimarisi olarak Redux'u seçtiğimden hiçbir zaman pişman olmadım. Redux'u bir UIKit uygulamasında kullanırken en zorlu kısımlar mağaza ile çalışmak, verileri almak ve almak ve bunları görünümlerinizle/bileşenlerinizle eşlemektir. Bunu yapmak için bir tür bağlayıcı kitaplığı oluşturmam gerekiyordu (ReSwift ve ReKotlin'i kullanarak). İyi çalışıyor, ancak oldukça fazla kod var. Maalesef (henüz) açık kaynak değil.
İyi haberler! Redux'u kullanmayı planlıyorsanız SwiftUI ile ilgili endişelenmeniz gereken tek şey mağazalar, durumlar ve düşürücülerdir. @EnvironmentObject sayesinde mağazayla etkileşim tamamen SwiftUI tarafından hallediliyor. Yani mağaza bir BindableObject ile başlar.
Basit bir Swift paketi oluşturdum, Redux'un temel kullanımını sağlayan. Benim durumumda bu MovieSwiftUI'nin bir parçası. ben de , bu bileşeni kullanmanıza yardımcı olacaktır.
Nasıl çalışır?
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)
}
}Bir eylemi her tetiklediğinizde vites kutusunu etkinleştirirsiniz. Uygulamanın mevcut durumuna göre aksiyonları değerlendirecektir. Daha sonra eylem türüne ve verilere uygun olarak yeni bir değiştirilmiş durum döndürecektir.
Mağaza bir BindableObject olduğundan, PassthroughSubject tarafından sağlanan willChange özelliğini kullanarak değeri değiştiğinde SwiftUI'ye bildirimde bulunacaktır. Bunun nedeni, BindableObject'in bir PublisherType sağlaması gerekmesidir, ancak protokol uygulamasının yönetilmesinden sorumludur. Genel olarak, bu Apple'ın çok güçlü bir aracıdır. Buna göre, bir sonraki oluşturma döngüsünde SwiftUI, görünümlerin gövdesinin durum değişikliğine göre oluşturulmasına yardımcı olacaktır.
Aslında SwiftUI'nin tüm kalbi ve büyüsü budur. Artık bir duruma abone olan herhangi bir görünümde, görünüm, durumdan hangi verinin alındığına ve neyin değiştiğine göre oluşturulacaktır.
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())
}
}Uygulama başlatıldığında Mağaza, EnvironmentObject olarak eklenir ve daha sonra @EnvironmentObject kullanılarak herhangi bir görünümde erişilebilir. Türetilmiş özellikler uygulama durumundan hızlı bir şekilde alındığından veya hesaplandığından herhangi bir performans cezası yoktur.
Yukarıdaki kod, film posteri değiştiğinde görüntüyü değiştirir.
Ve bu aslında tek bir hat üzerinden yapılıyor ve bu sayede görüşler devlete bağlanıyor. İOS'ta ReSwift ile çalıştıysanız veya hatta React ile SwiftUI'nin büyüsünü anlayacaksınız.
Artık eylemi etkinleştirmeyi ve yeni durumu yayınlamayı deneyebilirsiniz. İşte daha karmaşık bir örnek.
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!]))
}
}
}
}Yukarıdaki kodda her IP için SwiftUI'nin .onDelete eylemini kullanıyorum. Bu, listedeki satırın silinecek normal iOS kaydırma işlemini görüntülemesine olanak tanır. Yani kullanıcı silme butonuna dokunduğunda ilgili eylem tetikleniyor ve film listeden kaldırılıyor.
Liste özelliği BindableObject durumundan türetildiği ve bir EnvironmentObject olarak enjekte edildiği için SwiftUI listeyi günceller çünkü ForEach, filmin hesaplanan özelliğiyle ilişkilendirilir.
MoviesState düşürücünün bir parçası:
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
}İndirgeyici, yukarıda belirtildiği gibi bir eylem gönderdiğinizde ve yeni bir durum döndürdüğünüzde yürütülür.
Henüz ayrıntılara girmeyeceğim; SwiftUI aslında neyi görüntüleyeceğini nasıl biliyor. Bunu daha derinlemesine anlamak için buna değer. SwiftUI'da. Ayrıca neden ve ne zaman kullanılacağını da ayrıntılı olarak açıklıyor , @Binding, ObjectBinding ve EnvironmentObject.
Skillbox şunları önerir:
- pratik kurs .
- Uygulamalı çevrimiçi kurs .
- İki yıllık uygulamalı kurs .
Kaynak: habr.com
