Reputation: 1262
I have a publisher which would need re-evaluating on day change, but should continue to emit values at any other time.
As such, I thought I could use a NotificationCenter
publisher for the UIApplication.significantTimeChangeNotification
notification and combine it with my publisher such that the combine emission process would re-run on either on data change or day change and hence re-evaluate the map filter. See a rough outline of that code below.
The problem is that there is no published event by NotificationCenter
at the point in time that this is setup and hence, none of the following map
etc calls actually evaluate. merge(with:)
won't work as the two publishers publish different types, but combineLatest(_:)
and zip(_:)
both won't emit events until both publishers have emitted a single event.
I can validate that my code operates as expected by adding NotificationCenter.default.post(name: UIApplication.significantTimeChangeNotification, object: nil)
after this code, but that is undesirable due to it potentially signalling other areas of the app that an actual time change has occurred when it hasn'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: { $0.id == today })
}
...remainder of combine code
Can this combination of publishers and evaluation of events occur with the current Swift combine framework? Like the behaviour I'd expect from merge(with:)
but where the publishers emit two different types.
edit: I found one solution where I map the notification publisher to a nil array
let dayChangePublisher = NotificationCenter.default
.publisher(for: UIApplication.significantTimeChangeNotification)
.map { _ ➝ [MyEntity]? in
return nil
}
And then use merge
and compactMap
to avoid passing any nil values on
let mergedPub = repo.$entities
.merge(with: dayChangePublisher)
.compactMap { entity -> MyEntity? in
let today = todaysDate()
return entities?.first { $0.id == today }
}
.share()
It works, but maybe a bit cumbersome if anyone has a better solution?
Upvotes: 3
Views: 2494
Reputation: 49590
If I understood your question, you need a combineLatest
that is not blocked by not having an initial value from one of the publishers.
You can achieve that with .prepend(value)
operator. In this case, since you don't care about the actual value, map first to Void
, then prepend a Void
. It would work like so:
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 })
}
//...
Upvotes: 6