Swift 合并尚未发送值的发布商
Swift combine publishers where one hasn't sent a value yet
我有一个发布者需要在日期更改时重新评估,但应该在任何其他时间继续发布值。
因此,我认为我可以使用 NotificationCenter
发布者来发送 UIApplication.significantTimeChangeNotification
通知,并将其与我的发布者结合,这样联合发布过程将重新 运行在数据更改或日期更改时重新评估地图过滤器。请参阅下面该代码的粗略概述。
问题是 NotificationCenter
在设置时没有发布事件,因此,以下 map
等调用的 none 实际评估。 merge(with:)
不会工作,因为两个发布者发布不同的类型,但是 combineLatest(_:)
和 zip(_:)
都不会发出事件,直到两个发布者都发出一个事件。
我可以通过在此代码后添加 NotificationCenter.default.post(name: UIApplication.significantTimeChangeNotification, object: nil)
来验证我的代码是否按预期运行,但这是不可取的,因为它可能会向应用程序的其他区域发出信号,表明实际时间已发生变化,但实际时间并未发生变化' t
private func todaysDate() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd"
return formatter.string(from: Date())
}
@Published var entities: [MyEntity]
let dayChangePublisher = NotificationCenter.default
.publisher(for: UIApplication.significantTimeChangeNotification)
$entities.combineLatest(dayChangePublisher)
.map(\.0) // Only pass on the entity for further operations
.map { entities -> MyEntity? in
let today = todaysDate()
return entities?.first(where: { [=11=].id == today })
}
...remainder of combine code
当前的 Swift 组合框架可以实现发布者和事件评估的这种组合吗?就像我期望 merge(with:)
的行为一样,但是发布者发出两种不同的类型。
编辑:
我找到了一个解决方案,我将通知发布者映射到一个 nil 数组
let dayChangePublisher = NotificationCenter.default
.publisher(for: UIApplication.significantTimeChangeNotification)
.map { _ ➝ [MyEntity]? in
return nil
}
然后使用 merge
和 compactMap
来避免在
上传递任何 nil 值
let mergedPub = repo.$entities
.merge(with: dayChangePublisher)
.compactMap { entity -> MyEntity? in
let today = todaysDate()
return entities?.first { [=13=].id == today }
}
.share()
有效,但如果有人有更好的解决方案,可能会有点麻烦?
如果我理解你的问题,你需要一个 combineLatest
不会因为没有来自其中一个发布者的初始值而被阻止。
您可以使用 .prepend(value)
运算符来实现。在这种情况下,由于您不关心实际值,因此首先映射到 Void
,然后在前面添加一个 Void
。它会像这样工作:
let dayChangePublisher = NotificationCenter.default
.publisher(for: UIApplication.significantTimeChangeNotification)
$entities.combineLatest(
dayChangePublisher
.map { _ in }
.prepend(()) // make sure to prepend a () value
)
.map(\.0) // Only pass on the entity for further operations
.map { entities -> MyEntity? in
let today = todaysDate()
return entities?.first(where: { [=10=].id == today })
}
//...
我有一个发布者需要在日期更改时重新评估,但应该在任何其他时间继续发布值。
因此,我认为我可以使用 NotificationCenter
发布者来发送 UIApplication.significantTimeChangeNotification
通知,并将其与我的发布者结合,这样联合发布过程将重新 运行在数据更改或日期更改时重新评估地图过滤器。请参阅下面该代码的粗略概述。
问题是 NotificationCenter
在设置时没有发布事件,因此,以下 map
等调用的 none 实际评估。 merge(with:)
不会工作,因为两个发布者发布不同的类型,但是 combineLatest(_:)
和 zip(_:)
都不会发出事件,直到两个发布者都发出一个事件。
我可以通过在此代码后添加 NotificationCenter.default.post(name: UIApplication.significantTimeChangeNotification, object: nil)
来验证我的代码是否按预期运行,但这是不可取的,因为它可能会向应用程序的其他区域发出信号,表明实际时间已发生变化,但实际时间并未发生变化' t
private func todaysDate() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd"
return formatter.string(from: Date())
}
@Published var entities: [MyEntity]
let dayChangePublisher = NotificationCenter.default
.publisher(for: UIApplication.significantTimeChangeNotification)
$entities.combineLatest(dayChangePublisher)
.map(\.0) // Only pass on the entity for further operations
.map { entities -> MyEntity? in
let today = todaysDate()
return entities?.first(where: { [=11=].id == today })
}
...remainder of combine code
当前的 Swift 组合框架可以实现发布者和事件评估的这种组合吗?就像我期望 merge(with:)
的行为一样,但是发布者发出两种不同的类型。
编辑: 我找到了一个解决方案,我将通知发布者映射到一个 nil 数组
let dayChangePublisher = NotificationCenter.default
.publisher(for: UIApplication.significantTimeChangeNotification)
.map { _ ➝ [MyEntity]? in
return nil
}
然后使用 merge
和 compactMap
来避免在
let mergedPub = repo.$entities
.merge(with: dayChangePublisher)
.compactMap { entity -> MyEntity? in
let today = todaysDate()
return entities?.first { [=13=].id == today }
}
.share()
有效,但如果有人有更好的解决方案,可能会有点麻烦?
如果我理解你的问题,你需要一个 combineLatest
不会因为没有来自其中一个发布者的初始值而被阻止。
您可以使用 .prepend(value)
运算符来实现。在这种情况下,由于您不关心实际值,因此首先映射到 Void
,然后在前面添加一个 Void
。它会像这样工作:
let dayChangePublisher = NotificationCenter.default
.publisher(for: UIApplication.significantTimeChangeNotification)
$entities.combineLatest(
dayChangePublisher
.map { _ in }
.prepend(()) // make sure to prepend a () value
)
.map(\.0) // Only pass on the entity for further operations
.map { entities -> MyEntity? in
let today = todaysDate()
return entities?.first(where: { [=10=].id == today })
}
//...