11import Yosemite
22import Storage
33import Combine
4+ import struct SwiftUI. Binding
45
56final class EditAddressFormViewModel : ObservableObject {
6-
77 /// Current site ID
88 ///
99 private let siteID : Int64
@@ -41,24 +41,26 @@ final class EditAddressFormViewModel: ObservableObject {
4141 // Listen only to the first emitted event.
4242 onLoadTrigger. first ( ) . sink { [ weak self] in
4343 guard let self = self else { return }
44- self . bindNavigationTrailingItemPublisher ( )
4544 self . bindSyncTrigger ( )
45+ self . bindSelectedCountryIntoFields ( )
46+ self . bindNavigationTrailingItemPublisher ( )
47+
4648 self . fetchStoredCountriesAndTriggerSyncIfNeeded ( )
47- self . updateFieldsWithOriginalAddress ( )
49+ self . setFieldsInitialValues ( )
4850 } . store ( in: & subscriptions)
4951 }
5052
5153 /// Original `Address` model.
5254 ///
5355 private let originalAddress : Address
5456
55- /// Address form fields
57+ /// Current selected country.
5658 ///
57- @Published var fields = FormFields ( )
59+ @Published private var selectedCountry : Yosemite . Country ?
5860
59- /// Tracks the current selected country
61+ /// Address form fields
6062 ///
61- private let selectedCountry = CurrentValueSubject < Yosemite . Country ? , Never > ( nil )
63+ @ Published var fields = FormFields ( )
6264
6365 /// Trigger to perform any one time setups.
6466 ///
@@ -80,7 +82,11 @@ final class EditAddressFormViewModel: ObservableObject {
8082 /// Creates a view model to be used when selecting a country
8183 ///
8284 func createCountryViewModel( ) -> CountrySelectorViewModel {
83- CountrySelectorViewModel ( countries: countriesResultsController. fetchedObjects, selected: selectedCountry)
85+ let selectedCountryBinding = Binding (
86+ get: { self . selectedCountry } ,
87+ set: { self . selectedCountry = $0 }
88+ )
89+ return CountrySelectorViewModel ( countries: countriesResultsController. fetchedObjects, selected: selectedCountryBinding)
8490 }
8591
8692 /// Update the address remotely and invoke a completion block when finished
@@ -123,7 +129,7 @@ extension EditAddressFormViewModel {
123129 var country : String = " "
124130 var state : String = " "
125131
126- mutating func update( from address: Address , selectedCountry : Yosemite . Country ? ) {
132+ mutating func update( with address: Address ) {
127133 firstName = address. firstName
128134 lastName = address. lastName
129135 email = address. email ?? " "
@@ -134,11 +140,15 @@ extension EditAddressFormViewModel {
134140 address2 = address. address2 ?? " "
135141 city = address. city
136142 postcode = address. postcode
137- country = selectedCountry? . name ?? address. country
138143 state = address. state
139144 }
140145
141- func toAddress( selectedCountry: Yosemite . Country ? ) -> Address {
146+ mutating func update( with selectedCountry: Yosemite . Country ? ) {
147+ country = selectedCountry? . name ?? country
148+ }
149+
150+
151+ func toAddress( selectedCountry: Yosemite . Country ? ) -> Yosemite . Address {
142152 Address ( firstName: firstName,
143153 lastName: lastName,
144154 company: company. isEmpty ? nil : company,
@@ -155,39 +165,45 @@ extension EditAddressFormViewModel {
155165}
156166
157167private extension EditAddressFormViewModel {
158- func updateFieldsWithOriginalAddress( ) {
159- updateSelectedCountryFromOriginalAddress ( )
160- fields. update ( from: originalAddress, selectedCountry: selectedCountry. value)
161- }
162-
163- /// Updates the `selectedCountry` subject by matching the address country code in our stored countries.
168+ /// Set initial values from `originalAddress` using the stored countries to compute the current selected country.
164169 ///
165- func updateSelectedCountryFromOriginalAddress( ) {
166- selectedCountry. value = countriesResultsController. fetchedObjects. first { $0. code == originalAddress. country }
170+ func setFieldsInitialValues( ) {
171+ selectedCountry = countriesResultsController. fetchedObjects. first { $0. code == originalAddress. country }
172+ fields. update ( with: originalAddress)
167173 }
168174
169175 /// Calculates what navigation trailing item should be shown depending on our internal state.
170176 ///
171177 func bindNavigationTrailingItemPublisher( ) {
172- Publishers . CombineLatest ( $fields, performingNetworkRequest)
173- . map { [ originalAddress, selectedCountry ] fields, performingNetworkRequest -> NavigationItem in
178+ Publishers . CombineLatest3 ( $fields, performingNetworkRequest, $selectedCountry )
179+ . map { [ originalAddress] fields, performingNetworkRequest, selectedCountry -> NavigationItem in
174180 guard !performingNetworkRequest else {
175181 return . loading
176182 }
177- return . done( enabled: originalAddress != fields. toAddress ( selectedCountry: selectedCountry. value ) )
183+ return . done( enabled: originalAddress != fields. toAddress ( selectedCountry: selectedCountry) )
178184 }
179185 . assign ( to: & $navigationTrailingItem)
180186 }
181187
188+ /// Update published fields when the selected country is updated.
189+ ///
190+ func bindSelectedCountryIntoFields( ) {
191+ $selectedCountry
192+ . sink { [ weak self] newCountry in
193+ self ? . fields. update ( with: newCountry)
194+ }
195+ . store ( in: & subscriptions)
196+ }
197+
182198 /// Fetches countries from storage, If there are no stored countries, trigger a sync request.
183199 ///
184200 func fetchStoredCountriesAndTriggerSyncIfNeeded( ) {
185201 // Initial fetch
186202 try ? countriesResultsController. performFetch ( )
187203
188- // Updates the selected country when the data store changes.
204+ // Updates the initial fields when/if the data store changes(after sync) .
189205 countriesResultsController. onDidChangeContent = { [ weak self] in
190- self ? . updateSelectedCountryFromOriginalAddress ( )
206+ self ? . setFieldsInitialValues ( )
191207 }
192208
193209 // Trigger a sync request if there are no countries.
0 commit comments