Погружение в Combine: последовательные и объединяющие операторы для улучшения процесса разработки iOS приложений
Добрый день! Я — Иваев Зафар, iOS разработчик в компании Usetech. В этой статье мы узнаем как фреймворк Combine помогает нам разрабатывать функционал приложения с помощью встроенных функций — операторов. Итак, мы покроем следующие типы операторов:
- Последовательные операторы
- Объединяющие операторы
Последовательные операторы
- .first , .first(where:)
- .last , .last(where:)
- .output(at:) , .output(in:)
- .count
- .contains , .contains(where:)
- .allSatisfy
- .reduce
1. first
Оператор .first позволяет нам получить первый элемент из последовательности:
import Foundation import Combine var subscriptions = Set() func firstExample() < let intPublisher = [10, 20, 100, 200].publisher intPublisher .first() .sink(receiveValue: < print(«First: ($0)») >) .store(in:
В результате, 10 принтится в консоли:
Основы многопоточности. Конкурентное и Параллельное исполнения задач. В чем разница?
Мы так же можем указать предикат, используя .first(where) версию оператора:
func firstWhereExample() < let intPublisher = [23, 33, 50, 27, 101, 108].publisher intPublisher .first(where: < $0.isMultiple(of: 2) >) .sink(receiveValue: < print($0) >) .store(in:
Как и ожидалось, консоль выводит значение 50:
2. last
Так же, как мы получили первый элемент последовательности, мы можем получить и последний:
func lastExample() < let intPublisher = [10, 20, 100, 200].publisher intPublisher .last() .sink(receiveValue: < print(«Last: ($0)») >) .store(in:
Точно так же мы можем предоставить условие, используя .last(where:) вариант:
func lastWhereExample() < let intPublisher = [23, 33, 50, 27, 101, 108].publisher intPublisher .last(where: < $0.isMultiple(of: 2) >) .sink(receiveValue: < print($0) >) .store(in:
Мы видим, что значение 108 было выведено в консоли, поскольку это последний элемент, удовлетворяющий предикату:
3. output
Версия этого оператора, .output(at:) , получает определенный элемент по указанному индексу:
func outputAtExample() < let stringPublisher = [«A», «B», «C», «D»].publisher stringPublisher .output(at: 3) .sink(receiveValue: < print(«Output: ($0)») >) .store(in:
Индекс — 3. Следовательно, выводится буква «D»:
Доклад все о Combine за 45 мин с примерами кода! (2022 г.)
Мы можем получить все элементы, принадлежащие указанному диапазону, используя версию .output(in:) :
4. count
Как и его аналог из стандартной библиотеки Swift, оператор .count возвращает количество опубликованных значений последовательности:
func countExample() < let voidSubject = PassthroughSubject() voidSubject .count() .sink(receiveValue: < print(«Total ($0) events»)>) .store(in:
Как мы видим, мы отправили три события Void , поэтому в консоли было выведено 3 события:
5. contains
Оператор .contains возвращает true или false в зависимости от того, был ли найден конкретный элемент в последовательности:
func containsExample() < let letterPublisher = [«A», «B», «C», «D», «E»].publisher letterPublisher .contains(«Z») .sink(receiveValue: < print(«Does contain the specified character: ($0)») >) .store(in:
Здесь мы ищем букву «Z». Так как она не была найдена, получаем false :
Мы можем предоставить предикат, используя ковариант .contains(where:) :
func containsWhereExample() < let letterPublisher = [«a», «b», «C», «d», «e»].publisher letterPublisher .contains(where: < $0.first!.isUppercase >) .sink(receiveValue: < print(«Does contain an uppercase character: ($0)») >) .store(in:
Так как letterPublisher выпускает заглавную букву «C», в консоли выводится true :
6. allSatisfy
Подобно предыдущему оператору .contains, оператор .allSatisfy возвращает значение типа Bool . Однако он возвращает true только в том случае, если каждый отдельный элемент удовлетворяет предоставленному условию:
func allSatisfyExample() < let intPublisher = [3, 9, 27, 81, 244].publisher intPublisher .allSatisfy(< $0.isMultiple(of: 3) >) .sink(receiveValue: < print(«All numbers are divisible by 3: ($0)») >) .store(in:
В этом случае условию удовлетворяют все элементы, кроме одного. Следовательно, получаем false :
7. reduce
Последний последовательный оператор, .reduce , предоставляет мощный механизм для накопления элементов последовательности и возврата окончательного значения по завершении:
func reduceExample() < let intPublisher = [3, 9, 27, 81, 244].publisher intPublisher .reduce(0) < accumulated, value in accumulated + value >.sink(receiveValue: < print(«Sum: ($0)») >) .store(in:
Здесь мы вычисляем сумму всех элементов. Накопление увеличивается по мере поступления новых элементов. Результат — 364, сумма всех предоставленных целых чисел:
Мы можем сократить оператор .reduce следующим образом, что выдаст идентичный результат:
func reduceExample() < let intPublisher = [3, 9, 27, 81, 244].publisher intPublisher .reduce(0, +) .sink(receiveValue: < print(«Sum: ($0)») >) .store(in:
Объединяющие операторы
- .prepend
- .append
- .switchToLatest
- .merge(with:)
- .combineLatest
- .zip
1. prepend
Эта группа операторов позволяет нам отправлять события, значения или другие Publisher до событий исходного Publisher :
import Foundation import Combine var subscriptions = Set() func prependOutputExample() < let stringPublisher = [«World!»].publisher stringPublisher .prepend(«Hello») .sink(receiveValue: < print($0) >) .store(in:
Результат — Hello и World! принтятся в последовательном порядке:
Теперь добавим другой Publisher того же типа:
func prependPublisherExample() < let subject = PassthroughSubject() let stringPublisher = [«Break things!»].publisher stringPublisher .prepend(subject) .sink(receiveValue: < print($0) >) .store(in: Run code») subject.send(completion: .finished) >
Результат аналогичен предыдущему (обратим внимание, что нам необходимо отправить объекту событие .finished , чтобы оператор .prepend работал):
2. append
Оператор .append работает аналогично .prepend , но в этом случае мы добавляем значения к исходному Publisher :
func appendOutputExample() < let stringPublisher = [«Hello»].publisher stringPublisher .append(«World!») .sink(receiveValue: < print($0) >) .store(in:
В результате мы видим как Hello и World! выводятся в консоли:
Подобно тому, как мы добавляли другой Publisher раньше, у нас также есть такая же опция с оператором .append :
3. switchToLatest
Более сложный оператор .switchToLatest позволяет нам объединить серию Publisher в один поток событий:
func switchToLatestExample() < let stringSubject1 = PassthroughSubject() let stringSubject2 = PassthroughSubject() let stringSubject3 = PassthroughSubject() let subjects = PassthroughSubject() subjects .switchToLatest() .sink(receiveValue: < print($0) >) .store(in: 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 на основной PassthroughSubject .
- stringSubject1 получает значение A.
- Мы отправляем stringSubject2 основному PassthroughSubject , автоматически игнорируя события stringSubject1 c этого момента.
- Точно так же мы отправляем значения в stringSubject2 . После, подключаемся к stringSubject3 , что заставляет главного PassthroughSubject начать игнорировать события от stringSubject2 .
В результате у нас выводятся A, C, D и G:
Рассмотрим реальный пример: у нас есть текстовое поле поиска ( UITextField ), которое используется для определения доступности какого либо товара в ассортименте. Как только пользователь что-то вводит, мы запускаем запрос.
Проблема заключается в том, что если пользователь введет какое-либо значение, запрос будет осуществлен несмотря на новый ввод в текстовое поле. Наша цель — отменить предыдущий запрос, если пользователь успел ввести новое значение в поле:
func switchToLatestExample2() < func isAvailable(query: String) ->Future < return Future < promise in DispatchQueue.main.asyncAfter(deadline: .now() + 2) < promise(.success(Bool.random())) >> > let searchSubject = PassthroughSubject() searchSubject .print(«subject») .map < isAvailable(query: $0) >.print(«search») .switchToLatest() .sink(receiveValue: < print($0) >) .store(in: Query 1″) DispatchQueue.main.asyncAfter(deadline: .now() + 1) < searchSubject.send( «Query 2») >>
Для простоты примера, функция isAvailable возвращает случайное значение типа Bool после некоторой задержки.
Благодаря оператору .switchToLatest мы добиваемся того, чего хотим. Выводится только одно финальное значение Bool вместо двух.
4. merge(with:)
Мы используем .merge(with:) для объединения двух Publisher , как если бы мы получали значения только от одного:
func mergeWithExample() < let stringSubject1 = PassthroughSubject() let stringSubject2 = PassthroughSubject() stringSubject1 .merge(with: stringSubject2) .sink(receiveValue: < print($0) >) .store(in: A») stringSubject2.send(«B») stringSubject2.send(«C») stringSubject1.send(«D») >
В результате получается чередующаяся последовательность элементов:
5. combineLatest
Оператор .combineLatest публикует tuple , содержащий последнее значение каждого Publisher .
Рассмотрим следующий реальный пример: у нас есть текстовые поля для имени пользователя и пароля, а также кнопка, позволяющая пройти на следующий экран в приложении. Мы хотим держать кнопку отключенной до тех пор, пока имя пользователя не будет содержать не менее пяти символов, а пароль — не менее восьми символов. Этого можно легко добиться с помощью оператора .combineLatest :
func combineLatestExample() < let usernameTextField = CurrentValueSubject(«») let passwordTextField = CurrentValueSubject(«») let isButtonEnabled = CurrentValueSubject(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: print(«isButtonEnabled: ($0)») >.store(in: user») usernameTextField.send(«user12») passwordTextField.send(«12») passwordTextField.send(«12345678») >
Как только usernameTextField и passwordTextField получают user12 и 12345678 соответственно, условие удовлетворяется и кнопка активируется:
6. zip
Оператор .zip доставляет пару соответствующих значений от каждого Publisher . Скажем, мы хотим определить, передали ли оба Publisher одно и то же значение Int :
func zipExample() < let intSubject1 = PassthroughSubject() let intSubject2 = PassthroughSubject() let foundIdenticalPairSubject = PassthroughSubject() 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: print(«is identical: ($0)») >) .store(in:
У нас есть следующие соответствующие значения из intSubject1 и intSubject2 :
Последнее значение, 9, не выводится, поскольку intSubject1 еще не опубликовал соответствующее значение:
Спасибо за чтение! В следующей статье, мы рассмотрим еще два типа операторов, которые предоставляет нам Combine.
Источник: habr.com
Combined Community Codec Pack (CCCP)
The Combined Community Codec Pack (CCCP) — набор кодеков для воспроизведения мультимедиа файлов, включающий проигрыватель Media Player Classic Home Cinema. Кодеки CCCP разрабатывались с единственной целью – обеспечить корректное воспроизведение аниме с субтитрами, но благодаря своей простоте и универсальности этот программный пакет получил широкую известность, и по праву считается одним из лучших наборов кодеков для Windows.
Данные кодеки рассчитаны только на декодирование медиа контента (на воспроизведение), поэтому программный набор Combined Community Codec Pack не включает кодировщики и шифраторы, используемые для преобразования мультимедиа файлов.
CCCP не может похвастаться таким богатым набором кодеков, как, например, K-Lite Mega Codec Pack, но разработчиками СССР поддержка многих кодеков была упразднена неслучайно, так как для подавляющего большинства пользователей нет никакой необходимости в большом количестве кодеков, необходимых для операций, которые ими с большой долей вероятности никогда не будут востребованы.
Кодеки The Combined Community Codec Pack (CCCP) подойдут для воспроизведения файлов следующих популярных форматов: AVI, OGM, MKV, MP4, FLV, 3GP, MP1, MP2, MP3, AC3, DTS, AAC, LPCM, FLAC, TTA, MPEG-2, DivX, XviD, H.264, WMV9, FLV, MPEG-4 и некоторых других.
Установка СCСР чрезвычайно проста и не должна вызвать затруднений даже у начинающих пользователей ПК.
Скачать кодеки CCCP бесплатно, без регистрации.
The Combined Community Codec Pack (CCCP) — набор кодеков для воспроизведения мультимедиа файлов, включающий проигрыватель Media Player Classic Home Cinema.
Версия: CCCP 2015-10-18
Операционная система: Windows 10, 8.1, 8, 7, Vista
Дата релиза: 19 октября 2015
Статус программы: Бесплатная
Официальный сайт: www.cccp-project.net
Рейтинг: (14 оценок, среднее: 4,93 из 5)
Источник: alpinefile.ru
Что собой представляют кодеки Combined Community Codec Pack?
В Combined Community Codec Pack есть несколько отличий от популярного пакета кодеков K-Lite Codec Pack. Дело в том, что СССР содержит не все существующие кодеки, а только для наиболее распространённых видео форматов.
Поскольку кодеки разрабатываются независимо друг от друга разными компаниями, то, соответственно, никто их особо не тестирует на совместимость между собой. Впоследствии кодеки могут конфликтовать между собой, выводить нестабильную картинку, менять цвет изображения, искажать звук и т.д. Вот именно по этой причине был разработан СССР, который содержит несколько кодеков, способных воспроизвести практически все видеофайлы, при этом избегая ошибок и конфликтов между собой.
В состав пакета СССР входят:
1. Ffdshow — кодек
2. DirectShow – фильтр
3. Haali Media Splitter – фильтр
4. FLV Splitter – фильтр
5. VSFilter – фильтр для субтитров
Пакет СССР содержит в себе два видеоплеера – Media Player Classic HomeCinema и ZoomPlayer Free, что предоставляет небольшой, но всё же выбор для пользователей. Поддерживается воспроизведение субтитров, при необходимости просто удаляется из системы.
Перед установкой СССР необходимо удалить другие кодеки из системы, чтобы это снова же не привело к несовместимости при воспроизведении.
Источник: softobase.com
combyne – создайте свой лук 6.6.3
Combyne — подберите идеальный для себя наряд. В этом приложении, вы сможете подобрать для себя идеальный наряд на любой случай жизни. Вы увидите огромный выбор самой различной одежды. Следите за модой и выбирайте подходящую для вас одежду и прически. При этом, вы можете складывать различную одежду в один комплект, таким образом примеривая ее.
Следите за внешним видом известных персон. Вы можете создать такой наряд, который вам захочется. У приложения более чем 50 магазинов-партнеров модной одежды.
Лучший Telegram-канал про технологии (возможно)
Также, если вам понравилась какая-то вещь, то вы можете напрямую через приложение приобрести ее. В приложении есть встроенный чат, где вы можете найти единомышленников или просто посоветоваться о выборе одежды. Подобрав идеальный набор одежды, вы можете сохранить его, чтобы вернуться к нему в любой момент. Также, вы можете комментировать наряды людей, которые используют это приложение.
- Создавайте уникальные образы с различной одежды.
- Следите за известными личностями и их внешним видом.
- Общайтесь с единомышленниками во встроенном чате.
- Комментируйте стиль ваших друзей.
- Делитесь вашими нарядами.
- Сотрудничество с более чем 50 магазинами-партнерами.
Источник: trashbox.ru