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
    }

然后使用 mergecompactMap 来避免在

上传递任何 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 })
    }
    //...