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

How to use BehaviorRelay as an alternate to Variable in RxSwift?

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

假设我有一个正在进行的 webService 调用,我收到一大块数据,它是 JSON 数组,在逐个解析 JSON 对象时,我更新变量的值 属性

这是我的变量声明

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

每次我将变量更新为

时都会获取一个新元素
myFilter.value.append(newModel)

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

BehaviorRelay 问题

现在我的声明看起来像

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

但最大的问题是 myFilter.valuereadOnly。很明显

myFilter.value.append(newModel) 

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

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

self?.expertsFilter.accept(newModel)

以上语句给出错误引用

Can not convert the value of NewModel to expected arguement type [NewModel]

显然,它需要一个数组而不是单个元素。

解决方法:

方案一:

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

方案二:

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

方案三:

摆脱BehaviorRelay并使用BehaviorSubject/PublishSubject

前两个听起来令人沮丧,因为在解析每个元素时可能需要触发 UI 我等不及要解析整个响应。所以很明显solution1用处不大

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

问题:

因为 BehaviorRelay 被提议作为 Variable 的替代品,我陷入困境,我正确使用 accept 吗?有没有更好的方法来解决它?

请帮忙

我会做那样的事 -

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

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

parsed.bind(to: ui)

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

您是否考虑过简单地从中继上的现有值创建一个新数组,追加,然后调用 accept

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

基于 ,这里有一个方便的扩展:

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

我写这个扩展是为了用 BehaviorRelays 替换 Variables。您可以根据此模式添加您需要的任何方法以轻松迁移。

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 子句的想法来自

另请注意 indexElement.Index 类型,而不是 Int 或其他类型。

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

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

我创建了这个扩展,有两种方法可以方便迁移,以防你有一个数组变量并且你必须使用追加。

    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()

这种扩展怎么样:

extension BehaviorRelay {

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

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

myFilter.val.append(newModel)

On 变量曾经有:

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"