我想创建一个实现以下目的的Swift Combine发布者:
UserDefaults
Swift包)或 GRDB sqlite数据库值的更改来触发(使用GRDBCombine。UserDefaults
应该在GRDBCombine发布者中的数据库查询中使用。这是到目前为止我尝试过的简化版本:
func tasksPublisher() -> AnyPublisher<[Task], Never> {
Defaults.publisher(.myUserDefault)
.flatMap { change in
let myUserDefault = change.newValue
return ValueObservation
.tracking { db in
try Task.
.someFilter(myUserDefault)
.fetchAll(db)
}
.removeDuplicates()
.publisher(in: database)
.eraseToAnyPublisher()
}
.eraseToAnyPublisher()
}
但是,此发布者会产生以下错误(根据上述发布者的简化版本进行了编辑):
Cannot convert return expression of type 'AnyPublisher<Publishers.FlatMap<_, AnyPublisher<Defaults.KeyChange<Int>, Never>>.Output, Publishers.FlatMap<_, AnyPublisher<Defaults.KeyChange<Int>, Never>>.Failure>' (aka 'AnyPublisher<_.Output, Never>') to return type 'AnyPublisher<[Task], Never>'
我敢打赌,这两个发布商的价值观不同:[Task]
和Defaults.KeyChange<Int>
存在问题。但是,我找不到解决此问题的方法。
答案 0 :(得分:0)
假设您希望每次Defaults发布者发出更改时都启动一个新的数据库发布者,则需要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
失败类型,因为与所有I / O外部性一样,数据库不是100%可靠的。在Stack Overflow答案中,很难建议此时隐藏错误(例如,将错误变成空的Task数组),因为隐藏错误会阻止您的应用知道错误所在并做出相应的反应。
这里是一个低于版本的版本,可捕获数据库错误。这是我要使用的版本,假设该应用程序仅在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()
}