@@ -15,6 +15,10 @@ final class CountrySelectorViewModel: FilterListSelectorViewModelable, Observabl
1515 }
1616 }
1717
18+ /// Define if the view should show placeholders instead of the real elements.
19+ ///
20+ @Published private( set) var showPlaceholders : Bool = false
21+
1822 /// Command that powers the `ListSelector` view.
1923 ///
2024 let command = CountrySelectorCommand ( countries: [ ] )
@@ -34,6 +38,10 @@ final class CountrySelectorViewModel: FilterListSelectorViewModelable, Observabl
3438 return ResultsController < StorageCountry > ( storageManager: storageManager, sortedBy: [ countriesDescriptor] )
3539 } ( )
3640
41+ /// Trigger to sync countries.
42+ ///
43+ private let syncCountriesTrigger = PassthroughSubject < Void , Never > ( )
44+
3745 /// Storage to fetch countries
3846 ///
3947 private let storageManager : StorageManagerType
@@ -50,15 +58,17 @@ final class CountrySelectorViewModel: FilterListSelectorViewModelable, Observabl
5058 self . siteID = siteID
5159 self . storageManager = storageManager
5260 self . stores = stores
53- fetchAndBindCountries ( )
61+ bindSyncTrigger ( )
62+ bindStoredCountries ( )
5463 }
5564}
5665
5766// MARK: Helpers
5867private extension CountrySelectorViewModel {
59- /// Fetches & Binds countries from storage, If there are no stored countries, sync them from a remote source .
68+ /// Fetches & Binds countries from storage, If there are no stored countries, trigger a sync request .
6069 ///
61- func fetchAndBindCountries( ) {
70+ func bindStoredCountries( ) {
71+
6272 // Bind stored countries & command
6373 countriesResultsController. onDidChangeContent = { [ weak self] in
6474 guard let self = self else { return }
@@ -68,13 +78,46 @@ private extension CountrySelectorViewModel {
6878 // Initial fetch
6979 try ? countriesResultsController. performFetch ( )
7080
71- // Reset countries with fetched data or sync countries if needed.
72- if !countriesResultsController. isEmpty {
73- command. resetCountries ( countriesResultsController. fetchedObjects)
74- } else {
75- let action = DataAction . synchronizeCountries ( siteID: siteID, onCompletion: { _ in } )
76- stores. dispatch ( action)
81+ // Trigger a sync request if there are no countries.
82+ guard !countriesResultsController. isEmpty else {
83+ return syncCountriesTrigger. send ( )
84+ }
85+
86+ // Reset countries with fetched
87+ command. resetCountries ( countriesResultsController. fetchedObjects)
88+ }
89+
90+ /// Sync countries when requested. Defines the `showPlaceholderState` value depending if countries are being synced or not.
91+ ///
92+ func bindSyncTrigger( ) {
93+ syncCountriesTrigger
94+ . handleEvents ( receiveOutput: { // Set `showPlaceholders` to `true` before initiating sync.
95+ self . showPlaceholders = true // I could not find a way to assign this using combine operators. :-(
96+ } )
97+ . map { // Sync countries
98+ self . makeSyncCountriesFuture ( )
99+ . replaceError ( with: ( ) ) // TODO: Handle errors
100+ }
101+ . switchToLatest ( )
102+ . map { _ in // Set `showPlaceholders` to `false` after sync is done.
103+ false
104+ }
105+ . assign ( to: & $showPlaceholders)
106+ }
107+
108+ /// Creates a publisher that syncs countries into our storage layer.
109+ ///
110+ func makeSyncCountriesFuture( ) -> AnyPublisher < Void , Error > {
111+ Future < Void , Error > { [ weak self] promise in
112+ guard let self = self else { return }
113+
114+ let action = DataAction . synchronizeCountries ( siteID: self . siteID) { result in
115+ let newResult = result. map { _ in } // Hides the result success type because we don't need it.
116+ promise ( newResult)
117+ }
118+ self . stores. dispatch ( action)
77119 }
120+ . eraseToAnyPublisher ( )
78121 }
79122}
80123
0 commit comments