Szukaj…


Podstawy RxSwift

FRP lub Functional Reactive Programming ma kilka podstawowych terminów, które musisz znać.

Każdy kawałek danych może być reprezentowany jako Observable , który jest asynchronicznym strumieniem danych. Moc FRP polega na reprezentowaniu zdarzeń synchronicznych i asynchronicznych w postaci strumieni, Observable s i zapewnia ten sam interfejs do pracy z nim.

Zwykle Observable przechowuje kilka (lub nie ma) zdarzeń, które zawierają datę - .Next zdarzenia, a następnie można je pomyślnie zakończyć (. .Success ) lub z błędem (. .Error ).

Rzućmy okiem na następujący marmurowy schemat:

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

W tym przykładzie jest strumień wartości Int . W miarę upływu czasu wystąpiły trzy zdarzenia .Next , a następnie strumień zakończył się pomyślnie.

--X->

Powyższy schemat pokazuje przypadek, w którym nie zostały wyemitowane żadne dane, a zdarzenie .Error kończy Observable .

Zanim przejdziemy dalej, istnieje kilka przydatnych zasobów:

  1. RxSwift . Spójrz na przykłady, przeczytaj dokumenty i zacznij.
  2. Pokój RxSwift Slack ma kilka kanałów rozwiązywania problemów edukacyjnych.
  3. Pobaw się z RxMarbles, aby dowiedzieć się, co robi operator i który jest najbardziej przydatny w twoim przypadku.
  4. Spójrz na ten przykład , poznaj kod samodzielnie.

Tworzenie obserwowalnych

RxSwift oferuje wiele sposobów na stworzenie Observable , spójrzmy:

import RxSwift

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

Tak więc tworzone są obserwowalne. Trzymają tylko jedną wartość, a następnie kończą się sukcesem. Niemniej jednak nic się nie stało po jego utworzeniu. Dlaczego?

Istnieją dwa etapy pracy z Observable s: obserwujesz coś, aby utworzyć strumień, a następnie subskrybujesz strumień lub wiążesz go z czymś, co wchodzi w interakcję z nim.

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

Konsola wydrukuje:

.Next(12)
.Completed()

A jeśli chciałbym pracować tylko z danymi, które mają miejsce w zdarzeniach .Next , .Next operatora subscribeNext :

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

Jeśli chcę utworzyć obserwowalne wiele wartości, używam różnych operatorów:

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.

I wreszcie, może chcę Observable , który działa. Na przykład wygodnie jest zawinąć operację sieciową w Observable<SomeResultType> . Zobaczmy, jak można to osiągnąć:

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

Utylizacja

Po utworzeniu subskrypcji ważne jest, aby zarządzać jej prawidłowym zwolnieniem.

Doktorzy nam to powiedzieli

Jeśli sekwencja kończy się w skończonym czasie, brak wywołania dispose lub niestosowanie addDisposableTo (disposeBag) nie spowoduje trwałych wycieków zasobów. Jednak zasoby te będą wykorzystywane do momentu ukończenia sekwencji, albo przez zakończenie produkcji elementów, albo przez zwrócenie błędu.

Istnieją dwa sposoby zwolnienia zasobów.

  1. Korzystanie z disposeBag i operatora addDisposableTo .
  2. Korzystanie z operatora takeUntil .

W pierwszym przypadku ręcznie przekazujesz subskrypcję do obiektu DisposeBag , który poprawnie usuwa całą DisposeBag pamięć.

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

W rzeczywistości nie musisz tworzyć DisposeBag w każdej tworzonej klasie, wystarczy spojrzeć na projekt Społeczności RxSwift o nazwie NSObject + Rx . Za pomocą frameworka powyższy kod można przepisać w następujący sposób:

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

W drugim przypadku, jeśli zbiega się w czasie z subskrypcji self życia obiektu, jest to możliwe do wykonania przy użyciu utylizacji takeUntil(rx_deallocated) :

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

Wiązania

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

Za pomocą operatora combineLatest każdym razem, gdy element jest emitowany przez jednego z dwóch Observables , połącz najnowszy element emitowany przez każdego Observable . W ten sposób łączymy wynik tworzenia dwóch UITextField z tekstem "Greetings, \($0)" za pomocą interpolacji łańcuchów, aby później połączyć się z tekstem UILabel .

Możemy powiązać dane z dowolnym UITableView i UICollectionView w bardzo prosty sposób:

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

To jest opakowanie Rx wokół metody źródła danych cellForRowAtIndexPath . A także Rx dba o implementację metody numberOfRowsAtIndexPath , która jest wymaganą metodą w tradycyjnym znaczeniu, ale nie musisz jej tutaj wdrażać, o nią zadbano.

RxCocoa i ControlEvents

RxSwift zapewnia nie tylko sposoby kontrolowania danych, ale także reaktywne reprezentowanie działań użytkownika.

RxCocoa zawiera wszystko, czego potrzebujesz. Zawija większość właściwości komponentów interfejsu użytkownika w Observable , ale nie bardzo. Istnieje kilka ulepszonych Observable nazwie ControlEvent (które reprezentują zdarzenia) i ControlProperties (które reprezentują właściwości, niespodzianka!). Te rzeczy trzymają Observable strumienie pod maską, ale mają również pewne niuanse:

  • Nigdy nie zawodzi, więc nie ma błędów.
  • Complete sekwencję przy zwolnieniu kontroli.
  • Dostarcza zdarzenia do głównego wątku ( MainScheduler.instance ).

Zasadniczo możesz z nimi pracować jak zwykle:

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.

Jest to bardzo ważne, aby używać: tak długo, jak używasz Rx, zapomnij o rzeczach @IBAction , wszystkim, czego potrzebujesz, aby związać i skonfigurować jednocześnie. Na przykład metoda viewDidLoad kontrolera widoku jest dobrym kandydatem do opisania działania składników interfejsu użytkownika.

Ok, inny przykład: załóżmy, że mamy pole tekstowe, przycisk i etykietę. Chcemy, aby sprawdzić poprawność tekstu w polu tekstowym, gdy dotknij przycisk i wyświetlić wyniki w etykiecie. Tak, wygląda na to, że jest to kolejne zadanie z potwierdzeniem adresu e-mail, co?

Przede wszystkim bierzemy button.rx_tap ControlEvent:

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

Tutaj puste nawiasy pokazują krany użytkownika. Następnie bierzemy to, co jest zapisane w polu tekstowym za withLatestFrom operatora withLatestFrom (spójrz na to tutaj , wyobraź sobie, że górny strumień reprezentuje withLatestFrom użytkownika, dolny - tekst w polu tekstowym).

button.rx_tap.withLatestFrom(textField.rx_text)

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

Fajnie, mamy strumień ciągów do sprawdzania poprawności, emitowany tylko wtedy, gdy musimy sprawdzać poprawność.

Każdy Observable ma takie znane operatory, jak map lub filter , weźmiemy map aby sprawdzić poprawność tekstu. Utwórz samodzielnie funkcję validateEmail , użyj dowolnego wyrażenia regularnego, które chcesz.

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) 

Gotowy! Jeśli potrzebujesz więcej niestandardowej logiki (takiej jak wyświetlanie widoków błędów w przypadku błędu, przejście do kolejnego ekranu po sukcesie ...), po prostu subskrybuj końcowy strumień Bool i tam go zapisz.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow