Если вы хотите запускать нового издателя базы данных каждый раз, когда издатель по умолчанию выдает изменение, вам нужен оператор switchToLatest () .
Для этого оператора нужны ошибки обоих издателей. гармонизированы. Здесь, поскольку Defaults.publisher имеет тип ошибки Never
, мы можем использовать оператор setFailureType (to:) , чтобы объединить тип ошибки издателя базы данных: Error
.
Это дает:
func tasksPublisher() -> AnyPublisher<[Task], Error> {
Defaults
.publisher(.myUserDefault)
.setFailureType(to: Error.self)
.map({ change -> DatabasePublishers.Value<[Task]> in
let myUserDefault = change.newValue
return ValueObservation
.tracking { db in
try Task
.someFilter(myUserDefault)
.fetchAll(db)
}
.removeDuplicates()
.publisher(in: database)
})
.switchToLatest()
.eraseToAnyPublisher()
}
Обратите внимание, что возвращаемый издатель имеет тип ошибки Error
, поскольку база данных не на 100% надежна, как и все внешние эффекты ввода / вывода. В ответе «Переполнение стека» трудно порекомендовать скрыть ошибки на этом этапе (например, превратив их в пустой массив задач), поскольку сокрытие ошибок не позволяет вашему приложению узнать, что не так, и реагировать соответствующим образом.
Ниже приведена версия, которая содержит ошибки базы данных. Я бы использовал эту версию, предполагая, что приложение просто не может работать, когда SQLite не работает: иногда бесполезно делать вид, что такие низкоуровневые ошибки могут быть перехвачены и обработаны удобным для пользователя способом.
// Traps on database error
func tasksPublisher() -> AnyPublisher<[Task], Never> {
Defaults
.publisher(.myUserDefault)
.map({ change -> AnyPublisher<[Task], Never> in
let myUserDefault = change.newValue
return ValueObservation
.tracking { db in
try Task
.someFilter(myUserDefault)
.fetchAll(db)
}
.removeDuplicates()
.publisher(in: database)
.assertNoFailure("Unexpected database failure")
.eraseToAnyPublisher()
})
.switchToLatest()
.eraseToAnyPublisher()
}