Clifton Labrum
Clifton Labrum

Reputation: 14060

Be Notified of Multiple ObservableObject Property Changes in Combine

I have a Swift Combine question. Let’s say I have an ObservableObject with a few properties like this:

class AppState: ObservableObject{
  static let shared = AppState()
  @Published var a: Object?
  @Published var b: Object?
  @Published var c = [Object]()
}

I know I can be notified if a single object changes like this:

myCancellable = AppState.shared.$a.sink { a in
  //Object 'a' changed
}

But is there a way to watch multiple properties and respond if any of them change?

Something like:

myCancellable = AppState.shared.[$a, $b, $c].sink { a, b, c in
  //Objects 'a', 'b', or 'c' changed
}

Upvotes: 1

Views: 1567

Answers (3)

Clifton Labrum
Clifton Labrum

Reputation: 14060

Thanks for the great answers. Another idea I stumbled across was to just store my objects in a struct and observe that.

struct Stuff{
  var a: Object?
  var b: Object?
  var c = [Object]()
}

Then in my AppState class:

class AppState: ObservableObject{
  static let shared = AppState()

  @Published var stuff: Stuff!
}

Then in the publisher:

myCancellable = AppState.shared.$stuff.sink { stuff in
  print(stuff.a)
  print(stuff.b)
  print(stuff.c)
}

I like this approach since I don't want to observe everything that might change in the AppState class (which is probably an indication I should break it into smaller pieces). This seems to be working well so far.

Upvotes: 0

jnpdx
jnpdx

Reputation: 52312

Assuming that a, b, and c are the same type (which Asperi pointed out to me they are not in the example, since c is an array), you can use Publishers.MergeMany:

myCancellable = Publishers.MergeMany([$a,$b,$c]).sink { newValue in 

}

There are also more specific versions of this function for a set number of arguments. For example, in your case, you could use Publishers.Merge3

Often, in examples I've seen online, you'll see Publishers.MergeMany followed by collect(), which gathers results from all of the publishers before moving on, publishing the results as an array. From your question, though, it doesn't sound like this will meet your needs, as you want a notification each time a singular member changes.

Upvotes: 2

Asperi
Asperi

Reputation: 257493

The possible variant is to observe any published changes of entire object, like

myCancellable = AppState.shared
    .objectWillChange.sink {
       // react here
    }

Upvotes: 1

Related Questions