У меня есть следующая проблема:
struct Item {
var foo: Int
init(_ iFoo: Int = 0){
self.foo = iFoo
}
}
class TestObject {
@Published var items = [Item(1), Item(2), Item(3), Item(4)]
private var avg:Double = 0.0{
didSet{
print("didSet: avg: '\(self.avg)'")
}
}
private var cancellableSet: Set<AnyCancellable> = []
private var isItemChangedPublisher: AnyPublisher<[Item], Never>{
self.$items
.eraseToAnyPublisher()
}
init(){
self.isItemChangedPublisher
.map{ items in
var sum = 0
for item in items{
sum += item.foo
}
return Double(sum)/Double(items.count)}
.assign(to: \.avg, on: self)
.store(in: &cancellableSet)
}
func changeItem(at index: Int, to value: Int){
if self.items.count < index{
self.items.append(Item(value))
}else{
self.items[index].foo = value
}
}
func getAvg() -> Double{
//Request: Items changed --> Change Value of avg here
//Set Value of avg only if items has changed AND "Request" is called
// - don't set the new Value if Items has not changed and "Request" is called
// - don't set the new Value if Items has changed, but "Request" is not called
return self.avg
}
}
var bar = TestObject()
bar.changeItem(at: 2, to: 20)
bar.changeItem(at: 0, to: 3)
print("1. avg: '\(bar.getAvg())'")
bar.changeItem(at: 2, to: 20)
print("2. avg: '\(bar.getAvg())'")
bar.changeItem(at: 2, to: 30)
print("3. avg: '\(bar.getAvg())'")
bar.changeItem(at: 2, to: 20)
Значение var avg
устанавливается каждый раз, когда я изменяю массив элементов. Я понимаю, что это предполагаемое поведение.
Но есть ли способ обновить переменную avg
только в том случае, если изменился Массив элементов И вызвано что-то вроде «Запрос». Если были изменены только элементы, Переменная avg
не должна обновляться, также, если вызывается только «Запрос», но элементы не были изменены, Переменная не должна обновляться.
Надеюсь Понятия не имею, как это сделать.
Есть ли у вас идеи сделать это с помощью инфраструктуры комбайна или другого решения?
Редактировать - 23.Jan.2020:
Я мог бы сделать что-то подобное:
import Combine
struct Item: Equatable {
var foo: Int
init(_ iFoo: Int = 0){
self.foo = iFoo
}
}
class TestObject {
@Published var items = [Item(1), Item(2), Item(3), Item(4)]
private var newAverage: Double? {
didSet{
print("didSet: items changed --> newAverage: '\(String(describing: self.newAverage))'")
}
}
private var average:Double = 0.0{
didSet{
print("didSet: average: '\(self.average)'")
}
}
private var cancellable: AnyCancellable?
private var isItemChangedPublisher: AnyPublisher<[Item], Never>{
self.$items
.eraseToAnyPublisher()
}
init(){
cancellable = self.isItemChangedPublisher
.removeDuplicates()
.map{Double($0.map{$0.foo}.reduce(0, +))/Double($0.count)}
.sink{self.newAverage = $0}
}
func changeItem(at index: Int, to value: Int){
if self.items.count < index{
self.items.append(Item(value))
}else{
self.items[index].foo = value
}
}
func getAverage() -> Double{
if self.newAverage != nil{
self.average = self.newAverage!
self.newAverage = nil
}
return self.average
}
}
var bar = TestObject()
bar.changeItem(at: 2, to: 20)
bar.changeItem(at: 0, to: 20)
print("1. avg: '\(bar.getAverage())'")
bar.changeItem(at: 1, to: 20)
print("2. avg: '\(bar.getAverage())'")
bar.changeItem(at: 1, to: 20)
print("3. avg: '\(bar.getAverage())'")
bar.changeItem(at: 3, to: 20)
/*
prints:
didSet: items changed --> newAverage: 'Optional(2.5)'
didSet: items changed --> newAverage: 'Optional(6.75)'
didSet: items changed --> newAverage: 'Optional(11.5)'
didSet: average: '11.5'
didSet: items changed --> newAverage: 'nil'
1. avg: '11.5'
didSet: items changed --> newAverage: 'Optional(16.0)'
didSet: average: '16.0'
didSet: items changed --> newAverage: 'nil'
2. avg: '16.0'
3. avg: '16.0'
didSet: items changed --> newAverage: 'Optional(20.0)'
*/
Но я все еще ищу решение только с комбайном (без грязного решения с переменной newAverage
).
Я также попробовал решение с пользовательским DispatchQueue (это просто попытка, а не хорошее решение или идея):
import Combine
import SwiftUI
struct Item: Equatable {
var foo: Int
init(_ iFoo: Int = 0){
self.foo = iFoo
}
}
struct MyQueue {
// let queue = DispatchQueue(label: "myQueue", attributes: .concurrent, target: .global())
let queue = DispatchQueue(label: "myQueue")
init(){
self.queue.suspend()
}
func releaseData(){
self.queue.resume()
self.queue.suspend()
}
}
class TestObject {
@Published var items = [Item(1), Item(2), Item(3), Item(4)]
private var average:Double = 0.0{
didSet{
print("didSet: average: '\(self.average)'")
}
}
private var cancellable: AnyCancellable?
let myQueue = MyQueue()
private var isItemChangedPublisher: AnyPublisher<[Item], Never>{
self.$items
.eraseToAnyPublisher()
}
init(){
cancellable = self.isItemChangedPublisher
.removeDuplicates()
.map{ items in
Double(items.map{ $0.foo }.reduce(0, +))/Double(items.count)}
.buffer(size: 1, prefetch: .keepFull, whenFull: .dropOldest) //The Buffer changes nothing
.receive(on: self.myQueue.queue)
.assign(to: \.average, on: self)
}
func changeItem(at index: Int, to value: Int){
if self.items.count < index{
self.items.append(Item(value))
}else{
self.items[index].foo = value
}
}
func getAverage() -> Double{
self.myQueue.releaseData()
return self.average
}
}
var bar = TestObject()
bar.changeItem(at: 2, to: 20)
bar.changeItem(at: 0, to: 20)
print("1. avg: '\(bar.getAverage())'")
bar.changeItem(at: 1, to: 20)
print("2. avg: '\(bar.getAverage())'")
bar.changeItem(at: 1, to: 20)
print("3. avg: '\(bar.getAverage())'")
bar.changeItem(at: 3, to: 20)
/*
Prints:
didSet: average: '2.5'
1. avg: '2.5'
didSet: average: '6.75'
didSet: average: '11.5'
2. avg: '11.5'
didSet: average: '16.0'
3. avg: '16.0'
But im looking for:
didSet: average: '11.5' (because 2.5 and 6.5 are dropped)
1. avg: '11.5'
didSet: average: '16.0'
2. avg: '16.0'
3. avg: '16.0'
*/
, но это тоже не работает ...