Как сделать базовые привязки в ReactiveCocoa 3 и 4
Я недавно читал ReactiveCocoa v3, и я изо всех сил пытаюсь создать базовые вещи. Я уже прочитал журнал изменений, тесты, несколько вопросов SO и статьи Колина Эберхардта по этому вопросу. Тем не менее, я все еще не вижу примеров базовых привязок.
Скажем, у меня есть приложение, в котором представлено меню дня. Приложение использует RAC3 и шаблон MVVM.
Модель (меню)
У модели есть один простой метод для извлечения сегодняшнего меню. На данный момент это не делает каких-либо сетевых запросов, это в основном просто создает объект модели. Свойством mainCourse
является String
.
class func fetchTodaysMenu() -> SignalProducer<Menu, NoError> {
return SignalProducer {
sink, dispoable in
let newMenu = Menu()
newMenu.mainCourse = "Some meat"
sendNext(sink, newMenu)
sendCompleted(sink)
}
}
ViewModel (MenuViewModel)
Модель просмотра предоставляет различные переменные String
, позволяющие диспетчеру просмотра отображать меню. Позвольте просто добавить одно свойство для показа основного курса.
var mainCourse = MutableProperty("")
И добавим привязку для этого свойства:
self.mainCourse <~ Menu.fetchTodaysMenu()
|> map { menu in
return menu.mainCourse!
}
ViewController (MenuViewController)
И последнее, но не менее важное: я хочу представить это основное блюдо в представлении. Я добавлю для этого UILabel
.
var headline = UILabel()
И, наконец, я хочу установить свойство text
этого UILabel, наблюдая мою модель представления. Что-то вроде:
self.headline.text <~ viewModel.headline.producer
К сожалению, это не работает.
Вопросы
- Метод
fetchTodaysMenu()
возвращает SignalProducer<Menu, NoError>
, но что, если я хочу, чтобы этот метод возвращал SignalProducer<Menu, NSError>
? Это сделало бы мою привязку в моей модели представления неудачной, поскольку метод теперь может вернуть ошибку. Как я могу справиться с этим?
- Как уже упоминалось, текущая привязка в моем контроллере просмотра не работает. Я играл с созданием
MutableProperty
, который представляет свойство text
для UILabel
, но я так и не понял. Я также считаю, что неудобно или многословно создавать дополнительные переменные для каждого свойства, которое я хочу связать. Это не было необходимо в RAC2. Я намеренно также пытался избежать использования DynamicProperty
, но, может быть, я не должен? Я просто хочу найти правильный способ сделать RAC(self.headline, text) = RACObserve(self.viewModel, mainCourse);
.
Любые другие отзывы/рекомендации о том, как сделать эту базовую установку, высоко оценены.
Ответы
Ответ 1
Итак, после написания этого вопроса, Колин Эберхардт сделал часть 3 своей серии блога RAC3, которая содержит интересный и очень важный пример использования MVVM и RAC3. Сообщение можно найти здесь и исходный код здесь.
Основываясь на его работе, мне удалось ответить на мои вопросы:
-
Используя несколько иной подход, я могу сделать fetchTodaysMenu()
возвратом SignalProducer<Menu, NSError>
по мере необходимости. Вот как я тогда мог бы сделать в моей модели:
MenuService.fetchTodaysMenu()
|> observeOn(QueueScheduler.mainQueueScheduler)
|> start(next: { response in
self.mainCourse.put(response.mainCourse!)
}, error: {
println("Error \($0)")
})
-
Кажется, что нет привязок UIKit
от RAC3 beta 4. Колин сделал несколько расширений UIKit
, чтобы помочь ему выполнить эти привязки, которые я искал. Здесь можно найти здесь. Добавив их в свой проект, я смог сделать то, что хотел:
self.mainCourse.rac_text <~ self.viewModel.mainCourse
Обновление от 25 мая 2015 г.
После многократного использования ReactiveCocoa 3 я хотел бы еще раз ответить 1). Используя catch
, можно сделать это более декларативным образом. Я закончил реализацию небольшой вспомогательной функции для этого:
public func ignoreError<T: Any, E: ErrorType>(signalProducer: SignalProducer<T, E>) -> SignalProducer<T, NoError> {
return signalProducer
|> catch { _ in
SignalProducer<T, NoError>.empty
}
}
Функция преобразует любые NSError
в NoError
, что позволяет связывать, как я хотел, делая MenuService.fetchTodaysMenu() |> ignoreError
.
Я открываю свой проект, так как это может быть хорошей отправной точкой для других, которые смотрят на ReactiveCocoa 3.0:
https://github.com/s0mmer/TodaysReactiveMenu
Обновление 5 марта 2016 года
Как указано в комментариях, начиная с Swift 2, функция ignoreError
теперь выглядит следующим образом:
public func ignoreError() -> SignalProducer<Value, NoError> {
return flatMapError { _ in
SignalProducer<Value, NoError>.empty
}
}
Кроме того, была добавлена библиотека расширений, называемая Rex, где было добавлено нечто подобное.