如何在RxSwift中使用BehaviorRelay作为Variable的替代?

时间:2017-11-23 09:59:20

标签: ios11 rx-swift

从RxSwift4开始,Variable将移至Deprecated.swift,标记将来可能弃用VariableVariable提议的替代提案是BehaviorRelay。在发布这个问题时,由于我在网上找不到很多使用BehaviorRelay的教程,所以我在SO中发布了这样一个基本问题。

假设我正在进行webService调用,并且我收到一块JSONArray数据,在逐个解析JSON对象时我更新了我的Variable的value属性

这是我的变量声明

var myFilter = Variable<[MyFilterModel]>([MyFilterModel(data: "{:}")])

每次我将变量更新为

时获取新元素
myFilter.value.append(newModel)

当Variable绑定到CollectionView时,collectionVie将立即使用新添加的对象更新其UI。

使用BehaviorRelay

现在我的声明似乎是

var myFilter = BehaviorRelay<[MyFilterModel]>(value: [MyFilterModel(data: "{:}")])

但最大的问题是myFilter.value readOnly 。显然很明显

myFilter.value.append(newModel) 

不是解决方案。我发现我可以使用accept

但是现在当我尝试解析响应中的每个元素并更新myFilter的值

self?.expertsFilter.accept(newModel)

以上陈述给出错误引用

  

无法将NewModel的值转换为预期的争论类型   [newModel,并向]

显然,它期待一个数组,而不是一个单独的元素。

解决方法:

解决方案1:

因此,一个解决方案是在临时数组中累积所有响应,并在完成后触发self?.expertsFilter.accept(temporary_array)

解决方案2:

如果我必须在解析每个元素时向订阅者发送onNext事件,我需要将self?.expertsFilter的值复制到new Array,将新解析的元素添加到它并返回新数组。 / p>

解决方案3:

摆脱BehaviorRelay并使用BehaviorSubject / PublishSubject

前两个声音令人沮丧,因为在解析每个元素时可能需要触发UI我不能等到解析整个响应。因此,解决方案1显然没有多大用处。

第二种解决方案更加可怕,因为它每次发送onNext事件时都会创建一个新数组(我知道它是临时的并且将被释放)。

问题:

由于BehaviorRelay被提议作为Variable进入两难的替代方案,我正确使用accept?有没有更好的方法来解决它?

请帮忙

8 个答案:

答案 0 :(得分:15)

您是否考虑过根据接力上的现有值创建新数组,追加,然后调用accept

myFilter.accept(myFilter.value + [newModel])

答案 1 :(得分:7)

Dalton's answer为基础,这是一个方便的扩展名:

extension BehaviorRelay where Element: RangeReplaceableCollection {
    func acceptAppending(_ element: Element.Element) {
        accept(value + [element])
    }
}

答案 2 :(得分:6)

我写了这个扩展名,用Variable替换了BehaviorRelay。您可以根据此模式添加所需的任何方法来轻松迁移。

public extension BehaviorRelay where Element: RangeReplaceableCollection {

    public func insert(_ subElement: Element.Element, at index: Element.Index) {
        var newValue = value
        newValue.insert(subElement, at: index)
        accept(newValue)
    }

    public func insert(contentsOf newSubelements: Element, at index: Element.Index) {
        var newValue = value
        newValue.insert(contentsOf: newSubelements, at: index)
        accept(newValue)
    }

    public func remove(at index: Element.Index) {
        var newValue = value
        newValue.remove(at: index)
        accept(newValue)
    }
}

现在您写的是Variable.value.funcName,而不是BehaviorRelay.funcName

使用where Element: RangeReplaceableCollection子句的想法来自retendo's answer

还要注意,index的类型为Element.Index,而不是Int或其他类型。

答案 3 :(得分:2)

我会做那样的事情 -

let requests = PublishSubject<Observable<ServerResponse>>.create()
let responses: Observable<ServerResponse> = requests.switchLatest()

let parsed: Observable<[ParsedItem]> = responses
  .flatMap { Observable.from($0).map { parse($0) }.toArray() }

parsed.bind(to: ui)

// repeated part
let request1: Observable<ServerResponse> = servive.call()
request.onNext(request1)

答案 4 :(得分:2)

我创建了这些扩展名,并使用两种方法来简化迁移,以防万一您拥有数组变量并且必须使用append。

    extension BehaviorRelay where Element: RangeReplaceableCollection {

        func append(_ subElement: Element.Element) {
            var newValue = value
            newValue.append(subElement)
            accept(newValue)
        }

        func append(contentsOf: [Element.Element]) {
            var newValue = value
            newValue.append(contentsOf: contentsOf)
            accept(newValue)
        }

        public func remove(at index: Element.Index) {
            var newValue = value
            newValue.remove(at: index)
            accept(newValue)
        }

        public func removeAll() {
            var newValue = value
            newValue.removeAll()
            accept(newValue)
        }

    }

您这样称呼

    var things = BehaviorRelay<[String]>(value: [])
    things.append("aa")
    let otherThings = ["bb", "cc"]
    things.append(contentsOf: otherThings) 
    things.remove(at: 0)
    things.removeAll()

答案 5 :(得分:0)

AshKan的答案很好,但我来这里是为了寻找解决方案中缺少的方法。 附加:

extension BehaviorRelay where Element: RangeReplaceableCollection {

    func append(_ subElement: Element.Element) {
        var newValue = value
        newValue.append(subElement)
        accept(newValue)
    }

} 

答案 6 :(得分:0)

这种扩展如何:

extension BehaviorRelay {

    var val: Element {
        get { value }
        set { accept(newValue) }
    }
}

然后像使用Variable一样使用它,但是不要调用value,而是调用val

myFilter.val.append(newModel)

答案 7 :(得分:0)

On Variable 曾经有:

let variable = Variable("Hello RxSwift")
variable.value = "Change text" 
print(variable.value) // "Change text"

在 BehaviorRelay 上你必须使用:

let relay = BehaviorRelay(value: "Hello RxSwift")
relay.accept("Change text")
print(relay.value) // "Change text"