Кор дар барнома ба ман таҷрибаи зиёд дод ва дар маҷмӯъ ин як таҷрибаи мусбат буд. Ман тавонистам як барномаи комилан функсионалӣ нависам, дар моҳи сентябр ман онро такмил медиҳам ва онро дар AppStore ҳамзамон бо нашри iOS 13 нашр мекунам.
Redux, BindableObject ва EnvironmentObject
Ман тақрибан ду сол боз бо Redux кор мекунам, бинобар ин ман онро нисбатан хуб медонам. Махсусан, ман онро дар фронт барои Натиҷа вебсайт, инчунин барои таҳияи барномаҳои аслии iOS (Swift) ва Android (Kotlin).
Ман ҳеҷ гоҳ пушаймон нашудаам, ки Redux-ро ҳамчун меъмории ҷараёни маълумот барои сохтани барномаи SwiftUI интихоб кардам. Қисмҳои душвортарин ҳангоми истифодаи Redux дар як барномаи UIKit кор бо мағоза ва гирифтан ва дарёфт кардани маълумот ва харитасозии он ба назари/қисмҳои шумо мебошанд. Барои ин ман бояд як навъ китобхонаи пайвасткунакҳоро (бо истифода аз 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 аст, он 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
}
Редуктор ҳангоми фиристодани амал ва баргардонидани ҳолати нав, тавре ки дар боло гуфта шуд, иҷро мешавад.