web-dev-qa-db-fra.com

Comment utiliser BehaviorRelay comme alternative à la variable dans RxSwift?

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

45
Sandeep Bhandari

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])
17
daltonclaybrook

J'ai écrit cette extension pour remplacer Variables par BehaviorRelays. 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.

8
Ashkan Sarlak

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])
    }
}
7
retendo

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)
2
Maxim Volgin

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

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

} 
1
Reimond Hill