Recherche…


Bases RxSwift

La FRP, ou programmation réactive fonctionnelle, comporte certains termes de base que vous devez connaître.

Chaque donnée peut être représentée comme Observable , qui est un flux de données asynchrone. La puissance de FRP est en représentation des événements synchrones et asynchrones en tant que flux, Observable et fournissant la même interface pour travailler avec lui.

Habituellement, Observable détient plusieurs (ou aucun) événements qui contiennent les événements de date - .Next , puis il peut être terminé avec succès ( .Success ) ou avec une erreur ( .Error ).

Jetons un coup d'oeil au diagramme de marbre suivant:

--(1)--(2)--(3)|-->

Dans cet exemple, il y a un flux de valeurs Int . Au fil du temps, trois événements .Next se sont produits, puis le flux s'est terminé avec succès.

--X->

Le diagramme ci-dessus montre un cas où aucune donnée n'a été émise et l'événement .Error termine l' Observable .

Avant de continuer, il existe des ressources utiles:

  1. RxSwift . Regardez des exemples, lisez des documents et commencez.
  2. RxSwift Slack room dispose de quelques canaux pour résoudre les problèmes d’éducation.
  3. Jouez avec RxMarbles pour savoir ce que fait l'opérateur et lequel est le plus utile dans votre cas.
  4. Regardez cet exemple , explorez le code par vous-même.

Créer des observables

RxSwift propose de nombreuses façons de créer une Observable , jetons un coup d’œil:

import RxSwift

let intObservale = Observable.just(123) // Observable<Int>
let stringObservale = Observable.just("RxSwift") // Observable<String>
let doubleObservale = Observable.just(3.14) // Observable<Double>

Ainsi, les observables sont créés. Ils ne contiennent qu'une valeur et se terminent ensuite avec succès. Néanmoins, rien ne se passe après sa création. Pourquoi?

Travailler avec Observable s en deux étapes: vous observez quelque chose pour créer un flux, puis vous vous abonnez au flux ou le liez à quelque chose pour interagir avec lui.

Observable.just(12).subscribe {
    print($0)
}

La console imprimera:

.Next(12)
.Completed()

Et si je ne m'intéressais qu'au travail avec les données, qui ont lieu dans les événements .Next , j'utiliserais l'opérateur subscribeNext :

Observable.just(12).subscribeNext {
    print($0) // prints "12" now
}

Si je veux créer une observable de plusieurs valeurs, j'utilise différents opérateurs:

Observable.of(1,2,3,4,5).subscribeNext {
    print($0)
}
// 1
// 2
// 3
// 4
// 5

// I can represent existing data types as Observables also:
[1,2,3,4,5].asObservable().subscribeNext { 
    print($0) 
}
// result is the same as before.

Et enfin, peut-être que je veux une Observable qui fait du travail. Par exemple, il est pratique d’emballer une opération réseau dans Observable<SomeResultType> . Jetons un coup d'œil à ce que l'on peut réaliser:

Observable.create { observer in    // create an Observable ...
    MyNetworkService.doSomeWorkWithCompletion { (result, error) in
        if let e = error {
            observer.onError(e)    // ..that either holds an error
        } else {
            observer.onNext(result) // ..or emits the data
            observer.onCompleted()  // ..and terminates successfully.
        }
    }
    return NopDisposable.instance // here you can manually free any resources
                                //in case if this observable is being disposed.
}

Jeter

Une fois l’abonnement créé, il est important de gérer sa désallocation correcte.

Les docs nous ont dit que

Si une séquence se termine dans un temps fini, ne pas appeler disposer ou ne pas utiliser addDisposableTo (disposeBag) ne provoquera aucune fuite de ressource permanente. Cependant, ces ressources seront utilisées jusqu'à la fin de la séquence, soit en finissant la production d'éléments, soit en renvoyant une erreur.

Il existe deux manières de désallouer les ressources.

  1. Utilisation de l’ disposeBag s et addDisposableTo .
  2. Utilisation de l'opérateur takeUntil .

Dans le premier cas, vous passez manuellement l'abonnement à l'objet DisposeBag , qui efface correctement toute la mémoire prise.

let bag = DisposeBag()
Observable.just(1).subscribeNext { 
    print($0)
}.addDisposableTo(bag)

Vous n'avez pas vraiment besoin de créer des DisposeBag dans chaque classe que vous créez, jetez un coup d'œil au projet de la communauté RxSwift nommé NSObject + Rx . En utilisant le framework, le code ci-dessus peut être réécrit comme suit:

Observable.just(1).subscribeNext { 
    print($0)
}.addDisposableTo(rx_disposeBag)

Dans le second cas, si le temps d'abonnement coïncide avec la durée de vie de l'objet self , il est possible d'implémenter l'élimination en utilisant takeUntil(rx_deallocated) :

let _ = sequence
    .takeUntil(rx_deallocated)
    .subscribe {
        print($0)
    }

Fixations

Observable.combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 }
.map { "Greetings, \($0)" }
.bindTo(greetingLabel.rx_text)

En utilisant l'opérateur combineLatest chaque fois qu'un élément est émis par l'une des deux Observables , combinez le dernier élément émis par chaque Observable . Ainsi, nous combinons le résultat des deux nouveaux messages de création de UITextField avec le texte "Greetings, \($0)" utilisant une interpolation de chaîne pour se lier plus tard au texte d'un UILabel .

Nous pouvons lier des données à n'importe quel UITableView et UICollectionView de manière très simple:

viewModel
.rows
.bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in
    cell.title = viewModel.title
    cell.url = viewModel.url
}
.addDisposableTo(disposeBag)

C'est un wrapper Rx autour de la méthode de source de données cellForRowAtIndexPath . Et Rx prend également en charge l'implémentation de numberOfRowsAtIndexPath , qui est une méthode requise dans un sens traditionnel, mais vous n'avez pas à l'implémenter ici, c'est fait.

RxCocoa et ControlEvents

RxSwift fournit non seulement les moyens de contrôler vos données, mais également de représenter les actions de l'utilisateur de manière réactive.

RxCocoa contient tout ce dont vous avez besoin. Il encapsule la plupart des propriétés des composants de l'interface utilisateur dans Observable , mais pas vraiment. Il existe des Observable mis à niveau, appelés ControlEvent s (qui représentent des événements) et ControlProperties (qui représentent des propriétés, des surprises!). Ces objets contiennent des flux Observable sous le capot, mais ont également des nuances:

  • Il n'échoue jamais, donc pas d'erreurs.
  • Il Complete séquence sur le contrôle en cours de désallocation.
  • Il délivre des événements sur le thread principal ( MainScheduler.instance ).

Fondamentalement, vous pouvez travailler avec eux comme d'habitude:

button.rx_tap.subscribeNext { _ in   // control event
    print("User tapped the button!")
}.addDisposableTo(bag)

textField.rx_text.subscribeNext { text in // control property
    print("The textfield contains: \(text)")
}.addDisposableTo(bag)
// notice that ControlProperty generates .Next event on subscription
// In this case, the log will display 
// "The textfield contains: "
// at the very start of the app.

Ceci est très important à utiliser: tant que vous utilisez Rx, oubliez les éléments de @IBAction , tout ce dont vous avez besoin pour vous @IBAction et configurer en même temps. Par exemple, la méthode viewDidLoad de votre contrôleur de vue est un bon candidat pour décrire le fonctionnement des composants de l'interface utilisateur.

Ok, un autre exemple: supposons que nous ayons un champ de texte, un bouton et une étiquette. Nous voulons valider le texte dans le champ de texte lorsque nous tapons sur le bouton et afficher les résultats dans l’étiquette. Ouais, semble être une autre tâche de validation de courrier électronique, hein?

Tout d'abord, nous récupérons le bouton button.rx_tap ControlEvent:

----()-----------------------()----->

Ici, les parenthèses vides indiquent les touches utilisateur. Ensuite, nous prenons ce qui est écrit dans textField avec l'opérateur withLatestFrom (regardez-le ici , imaginez que le flux supérieur représente les taps des utilisateurs, le bas représente le texte dans le champ de texte).

button.rx_tap.withLatestFrom(textField.rx_text)

----("")--------------------("123")--->
//  ^ tap   ^ i wrote 123    ^ tap

Bien, nous avons un flux de chaînes à valider, émis uniquement lorsque nous devons valider.

Tout Observable a des opérateurs aussi familiers que map ou filter , nous prendrons la map pour valider le texte. Créez vous-même la fonction validateEmail , utilisez toutes les expressions que vous souhaitez.

button.rx_tap                                // ControlEvent<Void>
        .withLatestFrom(textField.rx_text)   // Observable<String>
        .map(validateEmail)                  // Observable<Bool>
        .map { (isCorrect) in
            return isCorrect ? "Email is correct" : "Input the correct one, please"
        }                                    // Observable<String>
        .bindTo(label.rx_text)              
        .addDisposableTo(bag) 

Terminé! Si vous avez besoin de plus de logique personnalisée (comme afficher des vues d'erreur en cas d'erreur, effectuer une transition vers un autre écran en cas de succès ...), abonnez-vous simplement au flux Bool final et écrivez-le.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow