从RxSwift4开始,Variable
将移至Deprecated.swift
,标记将来可能弃用Variable
。 Variable
提议的替代提案是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
?有没有更好的方法来解决它?
请帮忙
答案 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"