Русский
Русский
English
Статистика
Реклама

Перевод 6 объединяющих операторов Swift Combine, которые вам следует знать

Перевод статьи подготовлен в преддверии старта продвинутого курса iOS-Разработчик.



В этой статье мы рассмотрим шесть полезных операторов объединения в Combine. Мы сделаем это на примерах, экспериментируя с каждым из них в Xcode Playground.

Исходный код доступен в конце статьи.

Ну что ж, без лишних разглагольствований, давайте приступим.

1. prepend


Эта группа операторов позволяет нам добавлять (prepend дословно добавить в начало) к нашему исходному паблишеру события, значения или других паблишеров:

import Foundationimport Combinevar subscriptions = Set<AnyCancellable>()func prependOutputExample() {    let stringPublisher = ["World!"].publisher        stringPublisher        .prepend("Hello")        .sink(receiveValue: { print($0) })        .store(in: &subscriptions)}

Результат: Hello и World! выводятся в последовательном порядке:



Теперь давайте добавим другого издателя того же типа:

func prependPublisherExample() {    let subject = PassthroughSubject<String, Never>()    let stringPublisher = ["Break things!"].publisher        stringPublisher        .prepend(subject)        .sink(receiveValue: { print($0) })        .store(in: &subscriptions)        subject.send("Run code")    subject.send(completion: .finished)}

Результат аналогичен предыдущему (обратите внимание, что нам нужно отправить событие .finished в subject, чтобы оператор .prepend работал):



2. append


Оператор .append (дословно добавить в конец) работает аналогично .prepend, но в этом случае мы добавляем значения к исходному паблишеру:

func appendOutputExample() {    let stringPublisher = ["Hello"].publisher        stringPublisher        .append("World!")        .sink(receiveValue: { print($0) })        .store(in: &subscriptions)}

В результате мы видим Hello и World! выведенные на консоли:



Аналогично тому, как ранее мы использовали .prepend для добавления другого Publisherа, у нас также есть такая возможность и для оператора .append:



3. switchToLatest


Более сложный оператор .switchToLatest позволяет нам объединить серию паблишеров в один поток событий:

func switchToLatestExample() {    let stringSubject1 = PassthroughSubject<String, Never>()    let stringSubject2 = PassthroughSubject<String, Never>()    let stringSubject3 = PassthroughSubject<String, Never>()        let subjects = PassthroughSubject<PassthroughSubject<String, Never>, Never>()        subjects        .switchToLatest()        .sink(receiveValue: { print($0) })        .store(in: &subscriptions)        subjects.send(stringSubject1)        stringSubject1.send("A")        subjects.send(stringSubject2)        stringSubject1.send("B") // отброшено        stringSubject2.send("C")    stringSubject2.send("D")        subjects.send(stringSubject3)        stringSubject2.send("E") // отброшено    stringSubject2.send("F") // отброшено        stringSubject3.send("G")        stringSubject3.send(completion: .finished)}

Вот что происходит в коде:

  • Мы создаем три объекта PassthroughSubject, которым мы будем отправлять значения.
  • Мы создаем главный объект PassthroughSubject, который отправляет другие объекты PassthroughSubject.
  • Мы отправляем stringSubject1 на основной subject.
  • stringSubject1 получает значение A.
  • Мы отправляем stringSubject2 на основной subject, автоматически отбрасывая события stringSubject1.
  • Точно так же мы отправляем значения в stringSubject2, подключаемся к stringSubject3 и отправляем ему событие завершения.

В результате мы видим вывод A, C, D и G:



Для простоты, функция isAvailable возвращает случайное значение Bool после некоторой задержки.

func switchToLatestExample2() {    func isAvailable(query: String) -> Future<Bool, Never> {        return Future { promise in            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {                promise(.success(Bool.random()))            }        }    }        let searchSubject = PassthroughSubject<String, Never>()        searchSubject        .print("subject")        .map { isAvailable(query: $0) }        .print("search")        .switchToLatest()        .sink(receiveValue: { print($0) })        .store(in: &subscriptions)        searchSubject.send("Query 1")    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {        searchSubject.send( "Query 2")    }}

Благодаря оператору .switchToLatest мы достигаем того, чего хотим. Только одно значение Bool будет выведено на экран:



4. merge(with:)


Мы используем .merge(with:) для объединения двух Publishersов, как если бы мы получали значения только от одного:

func mergeWithExample() {    let stringSubject1 = PassthroughSubject<String, Never>()    let stringSubject2 = PassthroughSubject<String, Never>()        stringSubject1        .merge(with: stringSubject2)        .sink(receiveValue: { print($0) })        .store(in: &subscriptions)        stringSubject1.send("A")        stringSubject2.send("B")        stringSubject2.send("C")        stringSubject1.send("D")}

Результатом является чередующаяся последовательность элементов:



5. combineLatest


Оператор .combineLatest паблишит кортеж, содержащий последнее значение каждого издателя.

Чтобы проиллюстрировать это, рассмотрим следующий реальный пример: у нас есть имя пользователя, пароль UITextFields и кнопка продолжения. Мы хотим держать кнопку отключенной до тех пор, пока имя пользователя не будет содержать не менее пяти символов, а пароль не менее восьми. Мы можем легко добиться этого, используя оператор .combineLatest:

func combineLatestExample() {    let usernameTextField = CurrentValueSubject<String, Never>("")    let passwordTextField = CurrentValueSubject<String, Never>("")        let isButtonEnabled = CurrentValueSubject<Bool, Never>(false)        usernameTextField        .combineLatest(passwordTextField)        .handleEvents(receiveOutput: { (username, password) in            print("Username: \(username), password: \(password)")            let isSatisfied = username.count >= 5 && password.count >= 8            isButtonEnabled.send(isSatisfied)        })        .sink(receiveValue: { _ in })        .store(in: &subscriptions)        isButtonEnabled        .sink { print("isButtonEnabled: \($0)") }        .store(in: &subscriptions)        usernameTextField.send("user")    usernameTextField.send("user12")        passwordTextField.send("12")    passwordTextField.send("12345678")}

После того, как usernameTextField и passwordTextField получат user12 и 12345678 соответственно, условие удовлетворяется, и кнопка активируется:



6. zip


Оператор .zip доставляет пару соответствующих значений от каждого издателя. Допустим, мы хотим определить, паблишили ли оба паблишера одно и то же значение Int:

func zipExample() {    let intSubject1 = PassthroughSubject<Int, Never>()    let intSubject2 = PassthroughSubject<Int, Never>()        let foundIdenticalPairSubject = PassthroughSubject<Bool, Never>()        intSubject1        .zip(intSubject2)        .handleEvents(receiveOutput: { (value1, value2) in            print("value1: \(value1), value2: \(value2)")            let isIdentical = value1 == value2            foundIdenticalPairSubject.send(isIdentical)        })        .sink(receiveValue: { _ in })        .store(in: &subscriptions)        foundIdenticalPairSubject        .sink(receiveValue: { print("is identical: \($0)") })        .store(in: &subscriptions)        intSubject1.send(0)    intSubject1.send(1)        intSubject2.send(4)        intSubject1.send(6)    intSubject2.send(1)    intSubject2.send(7)        intSubject2.send(9) // Не отображено, потому что его пара еще не отправлена}

У нас есть следующие соответствующие значения из intSubject1 и intSubject2:

  • 0 и 4
  • 1 и 1
  • 6 и 7

Последние значение 9 не выводится, поскольку intSubject1 еще не опубликовал соответствующее значение:



Ресурсы


Исходный код доступен на Gist.

Заключение


Вас интересуют другие типы операторов Combine? Не стесняйтесь посещать мои другие статьи:

Источник: habr.com
К списку статей
Опубликовано: 12.08.2020 12:22:47
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании otus. онлайн-образование

Разработка под ios

Swift

Programming

Ios

Mobile

Xcode

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru