Я смотрю на пример использования SwiftUI с Combine: MVVM с руководством по Combine для iOS на raywenderlich.com.Реализация ViewModel дается так:
class WeeklyWeatherViewModel: ObservableObject, Identifiable {
// 2
@Published var city: String = ""
// 3
@Published var dataSource: [DailyWeatherRowViewModel] = []
private let weatherFetcher: WeatherFetchable
// 4
private var disposables = Set<AnyCancellable>()
init(weatherFetcher: WeatherFetchable) {
self.weatherFetcher = weatherFetcher
}
}
Итак, это имеет некоторый смысл для меня.В представлении, наблюдающем модель, экземпляр ViewModel объявляется как ObservedObject
следующим образом:
@ObservedObject var viewModel: WeeklyWeatherViewModel
И тогда можно использовать свойства @Published
в модели в body
определение вида выглядит следующим образом:
TextField("e.g. Cupertino", text: $viewModel.city)
В WeeklyWeatherViewModel
Комбинация используется для получения текста city
, создания запроса и преобразования его в [DailyWeatherRowViewModel]
.Вплоть до этого, все розовое и имеет смысл.
Когда я запутался, это то, что довольно много кода используется для:
- Запуск извлечения, когда
city
изменено. - Удерживайте
AnyCancellable
, который ищет данные о погоде. - Скопируйте вывод поиска погоды в
dataSource
с помощью sink
на выборке погоды Publisher`
Это выглядит так:
// More in WeeklyWeatherViewModel
init(
weatherFetcher: WeatherFetchable,
scheduler: DispatchQueue = DispatchQueue(label: "WeatherViewModel")
) {
self.weatherFetcher = weatherFetcher
_ = $city
.dropFirst(1)
.debounce(for: .seconds(0.5), scheduler: scheduler)
.sink(receiveValue: fetchWeather(forCity:))
}
func fetchWeather(forCity city: String) {
weatherFetcher.weeklyWeatherForecast(forCity: city)
.map { response in
response.list.map(DailyWeatherRowViewModel.init)
}
.map(Array.removeDuplicates)
.receive(on: DispatchQueue.main)
.sink(
receiveCompletion: { [weak self] value in
guard let self = self else { return }
switch value {
case .failure:
self.dataSource = []
case .finished:
break
}
},
receiveValue: { [weak self] forecast in
guard let self = self else { return }
self.dataSource = forecast
})
.store(in: &disposables)
}
Если я посмотрю в Combine определение свойства @Published
propertyWrapper, кажется, что все, что он делает, это projectedValue
, которыйэто Publisher
, что делает возможным, чтобы WeeklyWeatherViewModel
мог просто предоставить Publisher
извлекающие данные о погоде и для представления, чтобы использовать это непосредственно.Я не понимаю, почему копирование в dataSource
необходимо.
По сути, я ожидаю, что у SwiftUI будет возможность напрямую использовать Publisher, а для меня -быть в состоянии поместить этого издателя извне из реализации View, чтобы я мог внедрить его.Но я понятия не имею, что это такое.
Если это не имеет никакого смысла, эти цифры, как я запутался.Пожалуйста, дайте мне знать, и я посмотрю, смогу ли я уточнить мое объяснение.Спасибо!