Поиск…


Основы RxSwift

FRP или функционально-реактивное программирование, имеет некоторые основные термины, которые вам нужно знать.

Каждая часть данных может быть представлена ​​как Observable , которая представляет собой асинхронный поток данных. Сила FRP заключается в представлении синхронных и асинхронных событий как потоков, Observable s и предоставления одного и того же интерфейса для работы с ним.

Обычно Observable содержит несколько (или ни одного) событий, содержащих даты - .Next события, а затем он может быть успешно завершен ( .Success ) или с ошибкой ( .Error ).

Давайте посмотрим на следующую мраморную диаграмму:

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

В этом примере есть поток значений Int . По .Next происходит три .Next события, а затем поток завершается успешно.

--X->

Выше диаграмма показывает случай , где не было никаких данных , излучаемого и .Error событие завершает Observable .

Прежде чем двигаться дальше, есть некоторые полезные ресурсы:

  1. RxSwift . Посмотрите примеры, прочитайте документы и начните работу.
  2. В комнате RxSwift Slack есть несколько каналов для решения проблем образования.
  3. Играйте с RxMarbles, чтобы узнать, что делает оператор, и что наиболее полезно в вашем случае.
  4. Взгляните на этот пример , исследуйте код самостоятельно.

Создание наблюдаемых

RxSwift предлагает множество способов создания Observable , давайте посмотрим:

import RxSwift

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

Итак, наблюдаемые создаются. Они имеют только одно значение, а затем завершаются с успехом. Тем не менее, ничего не происходило после его создания. Зачем?

Существует два шага в работе с Observable s: вы наблюдаете что-то для создания потока, а затем подписываетесь на поток или связываете его с чем-то, чтобы взаимодействовать с ним.

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

Консоль будет печатать:

.Next(12)
.Completed()

И если мне интересно только работать с данными, которые происходят в .Next событиях, я бы использовал оператор subscribeNext :

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

Если я хочу создать наблюдаемое множество значений, я использую разные операторы:

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.

И, наконец, возможно, я хочу, чтобы Observable какую-то работу. Например, удобно Observable<SomeResultType> сетевую операцию в Observable<SomeResultType> . Давайте посмотрим, как можно добиться этого:

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

Располагая

После того, как была создана подписка, важно управлять ее правильным освобождением.

Документы сказали нам, что

Если последовательность завершается за конечное время, не вызывая распоряжаться или не использовать addDisposableTo (disposeBag), это не вызовет утечки постоянного ресурса. Тем не менее, эти ресурсы будут использоваться до тех пор, пока последовательность не завершится, либо завершив производство элементов, либо вернув ошибку.

Существует два способа освобождения ресурсов.

  1. Использование disposeBag s и addDisposableTo .
  2. Использование оператора takeUntil .

В первом случае вы вручную передаете подписку на объект DisposeBag , который правильно очищает всю DisposeBag память.

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

Вам фактически не нужно создавать DisposeBag s в каждом DisposeBag классе, просто взгляните на проект сообщества RxSwift с именем NSObject + Rx . Используя структуру, приведенный выше код можно переписать следующим образом:

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

Во втором случае, если время подписки совпадает с self жизнью объекта, можно осуществить с помощью утилизации takeUntil(rx_deallocated) :

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

Наручники

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

Используя оператор combineLatest каждый раз, когда элемент испускается одним из двух Observables объектов, объедините последний элемент, испускаемый каждым Observable . Таким образом, мы комбинируем результат двух UITextField , создающих новое сообщение с текстом "Greetings, \($0)" используя строчную интерполяцию, чтобы позже связать текст UILabel .

Мы можем привязать данные к любым UITableView и UICollectionView очень простым способом:

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

Это оболочка Rx вокруг cellForRowAtIndexPath источника данных cellForRowAtIndexPath . А также Rx заботится о реализации numberOfRowsAtIndexPath , который является необходимым методом в традиционном смысле, но вам не нужно его реализовывать здесь, это позаботится.

RxCocoa и ControlEvents

RxSwift предоставляет не только способы управления вашими данными, но и представление действий пользователя в реактивном режиме.

RxCocoa содержит все, что вам нужно. Он переносит большинство свойств компонентов пользовательского интерфейса в Observable s, но не совсем. Есть некоторые обновленные Observable s под названием ControlEvent s (которые представляют события) и ControlProperties (которые представляют свойства, удивление!). Эти вещи содержат Observable потоки под капотом, но также имеют некоторые нюансы:

  • Это никогда не терпит неудачу, поэтому ошибок нет.
  • Он будет Complete последовательность при освобождении элемента управления.
  • Он поставляет события в основной поток ( MainScheduler.instance ).

В принципе, вы можете работать с ними, как обычно:

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.

Это очень важно для использования: пока вы используете Rx, забудьте о вещах @IBAction , все, что вам нужно, вы можете сразу привязать и настроить. Например, метод viewDidLoad вашего контроллера представления является хорошим кандидатом для описания того, как работают пользовательские интерфейсы.

Хорошо, еще один пример: предположим, что у нас есть текстовое поле, кнопка и ярлык. Мы хотим проверить текст в текстовом поле, когда мы нажимаем кнопку, и отображаем результаты в метке. Да, похоже, еще одна задача проверки подлинности электронной почты, да?

Прежде всего, мы button.rx_tap ControlEvent:

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

Здесь пустые круглые скобки показывают пользовательские краны. Затем мы берем то, что написано в текстовом поле с withLatestFrom оператораLatestFrom (посмотрите на него здесь , представьте, что верхний поток представляет собой отладки пользователя, нижний - текст в текстовом поле).

button.rx_tap.withLatestFrom(textField.rx_text)

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

Приятно, у нас есть поток строк для проверки, испускаемый только тогда, когда нам нужно проверить.

В любом Observable есть такие знакомые операторы, как map или filter , мы берем map для проверки текста. Создайте функцию validateEmail самостоятельно, используйте любое регулярное выражение, которое вы хотите.

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) 

Готово! Если вам нужна дополнительная пользовательская логика (например, отображение ошибок в случае ошибки, переход на другой экран с успехом ...), просто подпишитесь на последний поток Bool и напишите там.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow