Если вы когда – либо интересовались фреймворком ReactiveCocoa, то заметили, что есть небольшое количество постов на тему реактивного программирования и фреймворка ReactiveCocoa, такие как Знакомство с ReactiveCocoa, или Лучший мир с ReactiveCocoa. До сегодняшнего дня, все эти посты были на тему ReactiveCocoa 2 версий, которая была написана на и для Objective-C. Сейчас все больше набирает популярность язык Swift, разработчики ReactiveCocoa усиленно работают над новой версией, которая будет написана на языке Swift и будет иметь некоторые функциональные особенности, которые являются фундаментальными для данного языка.
Я подозреваю, что многие из вас, как и я, с огромным желанием оставили Objective-C и перешли на Swift. Если вы хотели бы использовать ReactiveCocoa с новым языком, я вам настоятельно рекомендую попробовать использовать новую версию ReactiveCocoa. И я уверен, что наше сообщество получит огромную пользу от вклада, сделанного вами. С другой стороны, если вы работаете над большими бизнес приложениями в продакшн для определенного заказчика, я должен вам сказать – не делайте этого или хорошенько подумайте перед тем как использовать его. Но об этом мы поговорим дальше, если кого заинтересовал — прошу под кат.
Состояние ReactiveCocoa на сегодняшний день
Во время написания данной статьи, ReactiveCocoa (с этого времени именуемый, как RAC) находится в неопределенном состоянии. Когда первая версия языка Swift была опубликована, разработчики начали работу над третей версией RAC. Тем не менее, не так давно компания Apple выпустила Xcode 7.0, который содержит поддержку языка Swift версий 2.0. Это вызвано некоторыми критическими изменениями в RAC3, но также имеются некоторые значительные упрощения по отношению к новому API. Изменения подтолкнули разработчиков к началу работ над RAC4.
RAC4 еще далеко не в том состоянии, чтобы использовать ее для создания стабильных приложений, но многие уже это делают, также необходимо проявлять осторожность из-за того, что система часто изменяется. Протокол изменений гласит — “Возможно наличие критических изменений, поэтому будьте готовы к этому, перед тем как интегрировать его в приложение.” Но все же, если вы действительно готовы испытать RAC4, я поделюсь некоторыми советами, которые помогут начать использовать новую версию RAC.
Скажите досвидание RACSignal
В RAC2 все сигналы использовали тип RACSignal. Сигнал может быть в двух типов — hot (горячий) и cold (холодный). Горячий сигнал имеет, как минимум, одного подписчика, который обрабатывает отправляемые на него события, мне это напоминает замкнутую цепь, которая не имеет начала и не имеет конца. Холодный сигнал — это один из сигналов, который может не иметь подписчиков и поэтому не обеспечивает каких-либо действий или передачи информации. Его можно разбить на этапы, холодный сигнал не начинает свою работу пока на него кто-то не подпишется.
RAC4 разделяет концепцию сигналов на два различных вида: Signal Producers (Инициаторы сигналов) и Signals (сигналы). Процедура сигнала очень похожа на RACSignal; сигнал находится в бездействии до тех пор, пока он не будет запущен. Разработчики изменили терминологию слова subscribed на started, поскольку этот вариант описывает данное состояние сигнала более точно. Например, SignalProducer может иметь множественное количество подписчиков, которые подписаны для получения событий, но сигнал не стает активным, и подписчики не увидят никаких событий до тех пор, пока он не будет запущен по какой-то причине. Сигнал запускается вызовом метода start().
Другим новым типом является Signal. Signal подобен пожарному рукаву. Вы подключаете его к источнику и события протекают через него. Пока нет запуска сигнала, вы можете использовать его для работы подобно SignalProducer. Сигналы также не поддерживают никакой истории. Когда подписчик подписывается на события от сигнала, он получит все будущие события, но ни одного прошлого события. Сигналы очень полезны для передачи информации в реальном времени, что возникает как результат взаимодействия пользователя с приложением. Например, сигнал может отправлять цифры и сымволы, которые пользователь вводит в текстовое поле.
Метод Defer убрали
Вы также заметите, что RAC4 больше не содержит операцию defer (перенос). В отличие от вызова функции в RAC2, мы будем часто использовать блоки переноса, если потребуется запись функции, которая возвращает сигнал, а также выполняет некоторые другие операции (подобно регистрированию отлаженного текста), когда сигнал переходит в горячее состояние.
Опции defer в RAC4, больше не существует. Для демонстраци данного примера авторы предлагают нам рассмотреть started, подобный следующему.
На языке Swift:
func logIn(username: String, password: String) -> SignalProducer<String, NoError> {
return accountManager.logIn(username, password: password)
.on( started: {
print("Logging in...")
}
}
в отличие от, языка Objective-C
- (RACSignal *)logInWithUsername:(NSString *)username andPassword:(NSString *)password {
return [RACSignal defer:^{
NSLog("Logging in...");
return [accountManager logInUser:username withPassword:password];
}];
}
MutableProperties
Одна из особенностей, которая мне действительно нравится в RAC4 — это класс MutableProperty. MutableProperty является контейнером для объекта, изменяемый во время выполнения программы. Он также содержит инициатор сигнала, который автоматически отправляет каждое значение, которое было назначено подписчику.
Строгая типизация
Наиболее сложным для меня при переходе на RAC4 было привыкание к строгой типизации, связанной с сигналами. В RAC2 сигналы не имеют никакой информации про объекты или ошибки. Ограничение являлось только тем, что все объекты, отправленные на них, должны быть получены из NS объектов (не примитивов). В RAC4 сигнал определяется данным типом для значений, определяемых для него и для ошибок, что могут произойти. Например,
var signalA = SignalProducer<String, ReallyBadError> ...
определяет сигнал, который может быть отправлен только объектам типа String, и только ReallyBadError могут происходить при этом. Безопасность с точки зрения типов имеет некоторые преимущества в отношении того, что стает более понятно то, какая информация была отправлена, но в то же самое время это усложняет комбинирование сигналов.
В RAC2, вы можете легко взять сигнал, который отправляет Бананы, соединить его с сигналом, который отправляет Апельсины, и связать результат с сигналами которые отправляют Ананасы. Результирющий сигнал не будет иметь надлежащий вид, но код будет скомпилирован.
В RAC4, компилятор будет показывать ошибки, если вы будете пытаться вернуть сигналы, отправляющие Яблоки из функции, которая возможно возвращает сигнал, который отправляет Персики.
Комбинирование сигналов
Комбинирование сигналов является более сложным из-за того, что авторы избавились от хороших функций, которые использовали массивы сигналов для образования связывания, слияния, и т.п… Например в Objective-C, если вы хотите сделать объединения двух сигналов, вы должны выполнить следующее:
-(RACSignal *) bakeCakeWith:(NSArray *)ingredients {
RACSignal *preheatSignal = [self.oven preheat:@(350)];
RACSignal *bakeSignal = [self.baker combine:ingredients];
return [RACSignal concat:@[preheatSignal, bakeSignal];
}
Теперь в RAC4, вы должны выполнить следующее.
func bakeCake(ingredients: Array) -> SignalProducer<RACUnit, NoError> {
let (combinerSignal, combinerSink) = SignalProducer<RACUnit, NoError>.buffer(1)
sendNext(combinerSink, self.oven.preheat(350))
sendNext(combinerSink, self.baker.combine(ingredients))
return combinerSignal.flatten(.Concat)
}
Но вы можете создать собственную функцию, которая будет легко исполнять то же самое, что и в языке Objective-C.
Я определенно испытывал некоторое разочарование знакомясь с языком Swift и RAC4, но это был действительно веселый путь который я проделал. Я планирую продолжать двигаться дальше, и я призываю вас сделать то же самое.
Вот несколько действительно хороших источников где можно больше узнать о RAC:
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or let’s talk about ReactiveCocoa v4
An Introduction to ReactiveCocoa
MVVM WITH REACTIVECOCOA 3.0
Reactive Swift: upgrading to ReactiveCocoa 3.0
NightSoul
Не знаю как другим, но я все же предпочитаю писать без ReactiveCocoa, не нравится он мне. Дебажить сложно, особенно чужие проекты.
ajjnix
при хорошем разделении кода, отладка на RAC не сильно сложнее чем обычный асинхронный код
наши проекты пропитаны реактивом и включение человека на проект не является неподъемной задачей
i_user
чем больше используешь РАК, тем больше приходишь к тому, что его не просто сложно — а нельзя дебажить, отлаживать и т.п. и приходится продумывать другие паттерны повышения качества приложения. Например РАК — ооочень легко тестировать
PavelOsipov
Одна из неприятных черт RAC – это магия управлением времени жизни объектов. Из-за нее очень легко допустить утечку каких-нибудь RACDisposable. Но он все равно настолько хорош, что я написал библиотеку для трекинга утечек памяти и пишу юнит-тесты на утечки RACDisposable, как, например здесь.