A partir de RxSwift4, Variable
est déplacé vers Deprecated.Swift
, Marquant ainsi la dépréciation possible de Variable
. Une alternative proposée à Variable
est BehaviorRelay
. En postant cette question, je ne trouvais pas grand chose du tutoriel sur le Web en utilisant BehaviorRelay
mais en postant une question aussi fondamentale ici dans SO.
Supposons que je reçois un appel webService et que je reçoive un bloc de données au format JSONArray. Lors de l'analyse d'un objet JSON, un par un, je mets à jour la propriété value de ma variable.
Voici ma déclaration de variable
var myFilter = Variable<[MyFilterModel]>([MyFilterModel(data: "{:}")])
pour obtenir un nouvel élément chaque fois que je mettrais à jour ma variable comme
myFilter.value.append(newModel)
Comme Variable était liée à CollectionView, collectionVie mettrait immédiatement à jour son interface utilisateur avec le nouvel objet ajouté.
Problème avec BehaviorRelay
Maintenant, ma déclaration ressemble à
var myFilter = BehaviorRelay<[MyFilterModel]>(value: [MyFilterModel(data: "{:}")])
Mais le plus gros problème est que myFilter.value
Est readOnly. Donc évidemment
myFilter.value.append(newModel)
n'est pas une solution. J'ai compris que je pouvais plutôt utiliser accept
.
Mais maintenant, lorsque j'essaie d'analyser chaque élément en réponse et de mettre à jour la valeur de myFilter
self?.expertsFilter.accept(newModel)
La déclaration ci-dessus donne une citation d'erreur
Impossible de convertir la valeur de NewModel en type d'argument attendu [NewModel]
De toute évidence, il s’attend à un tableau et non à un élément individuel.
Solution de contournement:
Solution 1:
Donc, une solution consiste à accumuler toute la réponse dans un tableau temporaire et une fois cela fait, déclenchez self?.expertsFilter.accept(temporary_array)
Solution 2:
Si je dois envoyer un événement onNext
à un abonné lors de l'analyse de chaque élément, je dois copier la valeur de self? .ExpertsFilter dans un nouveau tableau, y ajouter le nouvel élément analysé et renvoyer le nouveau tableau.
Solution 3:
Débarrassez-vous de BehaviorRelay
et utilisez BehaviorSubject
/PublishSubject
.
Les deux premiers sons sont déprimants, car il peut être nécessaire de déclencher une interface utilisateur lors de l'analyse de chaque élément. Je ne peux pas attendre que toute la réponse soit analysée. Donc, évidemment, solution1 n'est pas très utile.
Deuxième solution est beaucoup plus horrible, car il crée un nouveau tableau (je sais son temporaire et sera publié) à chaque fois pour envoyer un événement onNext.
Question:
Parce que BehaviorRelay
est proposé comme alternative à Variable
suis en dilemme, utilise correctement accept
?? Y a-t-il une meilleure façon de le résoudre?
S'il vous plaît aider
Avez-vous envisagé de créer simplement un nouveau tableau à partir de la valeur existante sur le relais, d’ajouter, puis d’appeler accept
?
myFilter.accept(myFilter.value + [newModel])
J'ai écrit cette extension pour remplacer Variable
s par BehaviorRelay
s. Vous pouvez ajouter la méthode dont vous avez besoin en fonction de ce modèle pour migrer facilement.
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)
}
}
Au lieu de Variable.value.funcName
, Vous écrivez maintenant BehaviorRelay.funcName
.
L'idée d'utiliser la clause where Element: RangeReplaceableCollection
Vient de réponse de retendo
Notez également que le index
est de type Element.Index
, Pas Int
ou quoi que ce soit d'autre.
En s'appuyant sur réponse de Dalton , voici une extension pratique:
extension BehaviorRelay where Element: RangeReplaceableCollection {
func acceptAppending(_ element: Element.Element) {
accept(value + [element])
}
}
Je ferais quelque chose comme ça -
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)
J'ai créé ceux-ci cette extension, avec deux méthodes pour faciliter la migration dans le cas où vous avez une variable de tableau et que vous devez utiliser 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)
}
}
et vous l'appelez comme ça
var things = BehaviorRelay<[String]>(value: [])
things.append("aa")
let otherThings = ["bb", "cc"]
things.append(contentsOf: otherThings)
things.remove(at: 0)
things.removeAll()
AshKan répond que c’est génial, mais je suis venu ici à la recherche d’une méthode manquante dans la solution. Ajouter:
extension BehaviorRelay where Element: RangeReplaceableCollection {
func append(_ subElement: Element.Element) {
var newValue = value
newValue.append(subElement)
accept(newValue)
}
}