Получать уведомления о нескольких изменениях свойств ObservableObject в Combine

#swift #swiftui #combine

#swift #swiftui #объединить

Вопрос:

У меня вопрос о быстром объединении. Допустим, у меня есть ObservableObject с несколькими свойствами, подобными этому:

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

Я знаю, что могу получать уведомления, если один объект изменяется таким образом:

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

Но есть ли способ просмотреть несколько свойств и отреагировать, если какое-либо из них изменится?

Что-то вроде:

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

Ответ №1:

Возможным вариантом является наблюдение за любыми опубликованными изменениями всего объекта, например

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

Ответ №2:

Предполагая, что a , b , и c являются одним и тем же типом (на что мне указал Аспери, их нет в примере, поскольку c это массив), вы можете использовать Publishers.MergeMany :

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

}
 

Существуют также более конкретные версии этой функции для заданного количества аргументов. Например, в вашем случае вы могли бы использовать Publishers.Merge3

Часто в примерах, которые я видел в Интернете, вы увидите Publishers.MergeMany followed by collect() , который собирает результаты от всех издателей, прежде чем двигаться дальше, публикуя результаты в виде массива. Однако из вашего вопроса не похоже, что это будет соответствовать вашим потребностям, поскольку вы хотите получать уведомление каждый раз, когда изменяется отдельный элемент.

Комментарии:

1. MergeMany предназначен для одного типа, но c является массивом в начальном вопросе темы, поэтому он не будет компилироваться.

2. Ах, хорошая мысль. Пропустил эту деталь. Отредактированный пост, чтобы отразить это.

Ответ №3:

Спасибо за отличные ответы. Еще одна идея, на которую я наткнулся, заключалась в том, чтобы просто хранить мои объекты в a struct и наблюдать за этим.

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

Затем в моем AppState классе:

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

  @Published var stuff: Stuff!
}
 

Затем в издателе:

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

Мне нравится этот подход, поскольку я не хочу наблюдать за всем, что может измениться в AppState классе (что, вероятно, указывает на то, что я должен разбить его на более мелкие части). Похоже, пока это работает хорошо.