
Pärast osalemist WWDC 2019 State of the Union sessioonil otsustasin süveneda SwiftUI-sse. Olen sellele palju aega pühendanud ja nüüd olen alustanud reaalse rakenduse arendamist, mis võib olla kasulik laiemale kasutajaskonnale.
Nimelt MovieSwiftUI — see on rakendus uute ja vanade filmide otsimiseks ning nende kogumise kogumiks, kasutades . Olen alati armastanud filme ja isegi loonud ettevõtte, mis selles valdkonnas tegutseb, kuigi see oli juba ammu. Ettevõtet oli raske nimetada ägedaks, aga rakendust küll!
Tuletame meelde: kõigile «Habra» lugejatele — 10 000 rubla soodustus, kui registreerite end Skillboxi mis tahes kursusele promokoodi «Habr» abil.
Skillbox soovitab: Hariduslik veebikursus .
Nii et, mida MovieSwiftUI oskab?
- Interakteerub API-ga — see on midagi, mida peaaegu iga kaasaegne rakendus teeb.
- Laeb asünkroonselt andmeid päringute kaudu ja parsib JSON Swift mudelitesse, kasutades .
- Kuvab päringu kaudu laaditud pilte ja salvestab neid vahemällu.
- See iOS, iPadOS ja macOS rakendus tagab parima kasutajakogemuse nende opsüsteemide kasutajatele.
- Kasutaja saab genereerida andmeid, luua oma filmi nimekirju. Rakendus salvestab ja taastab kasutaja andmed.
- Esitlused, komponendid ja mudelid on selgelt eraldatud Reduxi mustrit kasutades. Andmevoog on siin ühesuunaline. Seda saab täielikult vahemällu salvestada, taastada ja kirjutada ümber.
- Rakendus kasutab põhikomponente SwiftUI-st, nagu TabbedView, SegmentedControl, NavigationView, Form, Modal jne. Samuti pakub see kohandatud esitusi, žeste, UI/UX-i.

Tegelikult on animatsioon sujuv, kuid gif tuli natuke kangeks.
Rakenduse arendamine andis mulle palju kogemusi ja üldiselt on see olnud positiivne. Suutsin kirjutada täielikult funktsionaalse rakenduse, mille parandan septembris ja avaldan AppStore'is koos iOS 13 ilmumisega.
Redux, BindableObject ja EnvironmentObject

Praeguseks olen Reduxiga töötanud juba umbes kaks aastat, nii et ma olen selles suhteliselt hästi kursis. Eelkõige kasutan seda frontendis veebisaidi jaoks , samuti natiivsete iOS (Swift) ja Android (Kotlin) rakenduste arendamiseks.
Ma ei ole kunagi kahetsenud Reduxi valimist andmevoo arhitektuuriks SwiftUI rakenduse loomisel. Kõige keerulisemad aspektid Reduxi kasutamisel UIKit rakenduses on töö tegemine store'iga, andmete saamine ja ekstraheerimine ning nende vastavusse viimine teie vaadete/komponentidega. Selleks pidin looma midagi sellist nagu konnektorite teegi (ReSwift ja ReKotlin põhjal). Töötab hästi, kuid koodi on üsna palju. Kahjuks ei ole see (praegu) avatud lähtekoodiga.
Head uudised! Ainus, millest tasub SwiftUI puhul muretseda – kui kavatsete kasutada Reduxi – on store, olekud ja reducereid. Suhtlemise store'iga võtab täielikult enda kanda SwiftUI tänu @EnvironmentObject'ile. Nii et store algab BindableObject'ist.
Loomulikult olen loonud lihtsa Swift paketi, , mis tagab Reduxi põhikasutuse. Minu puhul on see osa MovieSwiftUI-st. Samuti olen , mis aitab selle komponendi kasutamisel.
Kuidas see töötab?
final public class Store: BindableObject {
public let willChange = PassthroughSubject()
private(set) public var state: State
private func _dispatch(action: Action) {
willChange.send()
state = reducer(state, action)
}
}Iga kord, kui käivitate toimingu, aktiveerite reduktori. See hindab toimingut vastavalt rakenduse praegusele olekule. Seejärel tagastab see uue muudetud oleku vastavalt toimingu tüübile ja andmetele.
Kuna store on BindableObject, teavitab see SwiftUI oma väärtuse muutumisest, kasutades PassthroughSubject'i pakutavat willChange omadust. See on nii, sest BindableObject peab pakkuma PublisherType'i, kuid protokolli rakendamine vastutab selle haldamise eest. Üldiselt on see Apple'i poolt väga võimas tööriist. Seetõttu aitab SwiftUI järgmisel renderdamise tsüklil kuvada vaate keha vastavalt oleku muutusele.
Oactually, see on kõik — SwiftUI süda ja maagia. Nüüd igas vaates, mis on tellitud olekule, kuvatakse vaade vastavalt sellele, millised andmed on saadud olekust ja mis on muutunud.
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 tuuakse sisse kui EnvironmentObject rakenduse käivitamisel, ja see on seejärel saadaval igas vaates @EnvironmentObject abil. Tulemuslikkus ei lange, kuna tuletatud omadused tõmmatakse kiiresti välja või arvutatakse rakenduse olekust.
Ülaltoodud kood muudab pilti, kui filmi plakat muutub.
Ja seda tehakse tegelikult vaid ühe rea abil, millega ühendatakse vaated olekuga. Kui olete töötanud ReSwiftiga iOS-is või isegi Reactiga, siis mõistate, mis on SwiftUI võlu.
Nüüd saate proovida tegevust aktiveerida ja avaldada uut staatust. Siin on keerulisem näide.
struct CustomListDetail : View {
@EnvironmentObject var store: Store
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!]))
}
}
}
}Ülaltoodud koodis kasutan SwiftUI jaoks .onDelete toimet iga IP jaoks. See võimaldab loendi rida kuvada tavalist iOS-i pühkimist kustutamiseks. Seega, kui kasutaja puudutab kustutamisnuppu, käivitab see vastava toimingu ja eemaldab filmi loendist.
Kuna loendi omadus on tütar BindableObject seisundist ja süstitakse kui EnvironmentObject, uuendab SwiftUI loendi, kuna ForEach on seotud arvutatud omadusega movies.
Siin on osa reducerist MoviesState:
func moviesStateReducer(state: MoviesState, action: Action) -> MoviesState {
var state = state
switch action {
// muud toimingud.
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
}Reduceri teostatakse, kui saadate tegevuse ja tagastate uue oleku, nagu ülalpool öeldud.
Ma ei hakka veel üksikasjadesse laskuma — kust SwiftUI tegelikult teab, mida kuvada. Selle sügavamaks mõistmiseks tasub kohta SwiftUIS. Seal räägitakse ka põhjalikult, miks ja millal kasutada , @Binding, ObjectBinding ja EnvironmentObject.
Skillbox soovitab:
- Praktiline kursus .
- Rakenduslik veebikursus .
- Kaheaastane praktiline kursus .
Allikas: habr.com
