首页 > 解决方案 > Swift 合并尚未发送值的发布者

问题描述

我有一个发布者需要重新评估一天的变化,但应该在任何其他时间继续发出值。

因此,我认为我可以将NotificationCenter发布者用于UIApplication.significantTimeChangeNotification通知并将其与我的发布者结合起来,这样组合发射过程将在数据更改或日期更改时重新运行,从而重新评估地图过滤器。请参阅下面该代码的粗略轮廓。

问题是在设置的时间点没有发布事件NotificationCenter,因此,以下mapetc 调用都没有实际评估。merge(with:)由于两个发布者发布了不同的类型,因此将不起作用,但是在两个发布者都发出单个事件之前combineLatest(_:)zip(_:)两者都不会发出事件。

我可以通过在此代码之后添加来验证我的代码是否按预期运行NotificationCenter.default.post(name: UIApplication.significantTimeChangeNotification, object: nil),但这是不可取的,因为它可能会向应用程序的其他区域发出信号,表明实际时间发生了变化,而实际上并没有发生变化

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: { $0.id == today })
    }
    ...remainder of combine code

这种发布者和事件评估的组合能否在当前的 Swift 组合框架中发生?就像我期望的行为一样,merge(with:)但发布者发出两种不同的类型。

编辑:我找到了一种解决方案,将通知发布者映射到 nil 数组

let dayChangePublisher = NotificationCenter.default
    .publisher(for: UIApplication.significantTimeChangeNotification)
    .map { _ ➝ [MyEntity]? in
        return nil
    }

然后使用mergeandcompactMap来避免传递任何 nil 值

let mergedPub = repo.$entities
        .merge(with: dayChangePublisher)
        .compactMap { entity -> MyEntity? in
            let today = todaysDate()
            return entities?.first { $0.id == today }
        }
        .share()

它有效,但如果有人有更好的解决方案,可能有点麻烦?

标签: swiftcombine

解决方案


如果我理解您的问题,您需要一个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: { $0.id == today })
    }
    //... 

推荐阅读