Существует бесчисленное множество статей относительно шаблона MVVM в iOS, но немного о RxSwift, и мало кто акцентирует внимание на том, как выглядит паттерн MVVM на практике и как его реализовать.

ReactiveX — библиотека для создания асинхронных и основанных на событии программ при помощи наблюдаемой последовательности. — reactivex.io
RxSwift — относительно молодой фреймворк, который позволяет "реактивно программировать". Если Вы ничего о нем не знаете, тогда наведите справки, потому что функциональное реактивное программирование (FRP) набирает обороты, и не собирается останавливаться.
Способ соединения модели с ViewController при использований шаблона MVC часто выглядит, как некий хак. Вы, как правило, вызываете что-то вроде функции updateUI() в контроллере, когда думаете, что модель изменилась Это может привести к появлению несоответствий между моделью и ViewController, ненужным обновлениям и непонятным ошибкам.
Нам нужен ViewController, который покажет истинное состояние модели в любом случае. По сути, нам нужен ViewController, который будет являться простым прокси-сервером, который отобразит на экране данные согласно текущему состоянию модели.
Конечно, в большинстве приложений будут бесполезно, если они только отображают на экране модель. Нам необходимо получить данные из модели и подготовить их для отображения. Именно поэтому мы представляем класс ViewModel. ViewModel готовит все данные, которые необходимо отобразить на экране.
Но вот самое интересное: ViewModel ничего не знает о ViewController. Он никогда не ссылается на него и не имеет свойства напрямую. Вместо этого ViewController постоянно следит за любыми изменений ViewModel, и как только изменения произойдут, они сразу будут отображены на экране. Следует иметь в виду, что ViewController отображает на экране каждое свойство с ViewModel индивидуально, т.е. если вы хотите последовательно загрузить строку и изображение, вы сможете отобразить строку сразу посколько данные уже есть, и вам не придется ждать, пока изображение загрузится, чтобы отобразить их вместе.
Однако, ViewController отображает на экране не только данные, он также получает ввод данных от пользователя. Так как наш ViewController — просто прокси-сервер, он не может использовать эти данные, поэтому все, что он должен сделать, это передать их в ViewModel, и ViewModel сделает все остальное.

Это, в некотором смысле, односторонняя связь между ViewController и ViewModel. ViewController может видеть и обращаться к ViewModel, но у ViewModel нет ни малейшего представления о том, кто такой ViewController. Это значит, что Вы можете полностью удалить ViewController из своего приложения, и вся ваша логика будет работать так, как задумано!
Все это звучит прекрасно, но как мы это сделаем?
Давайте сделаем простое приложение, которое отображает прогноз погоды в городе, который вводит пользователь.
Эта статья предполагает, что вы знакомы с RxSwift. Если Вы ничего не знаете о нем, то смело читайте дальше, но я предлагаю больше узнать о ReactiveX.

У нас есть UITextField для ввода названия города и парочка UILabels, чтобы вывести на экран текущую температуру.
Примечание: для этого приложения я буду получать данные о погоде из OpenWeatherMap.
Так, наша модель будет классом Weather с несколькими свойствами name и degrees и инициализатором, который принимает объект JSON, который он анализирует и устанавливает свойства.
Примечание: SwiftyJSON является обязательным для разбора JSON в Swift.
Теперь мы должны позволить ViewModel управлять моделью посредством общедоступного (public) свойства searchText, к которому у ViewController позже будет доступ.
Наш searchText является объектом PublishSubject. Subject’ы одновременно являются и Observable и Observer. Другими словами, вы можете отправить им элементы, которые они могут повторно сгенерировать.
PublishSubjects уникальны, потому что когда данные передаются в PublishSubject, он рассылает их всем подписчикам, которые подписаны на него в данный момент. Нам нужно это, потому что в MVVM, в зависимости от жизненного цикла приложения, Observable в различных классах иногда могут получать элементы, прежде чем вы подпишетесь на них. Как только ViewController подписывается на свойство ViewModel, он должен увидеть, что с последним элементом все в порядке, чтобы вывести его на экран, и наоборот.
Теперь мы должны объявить свойство в ViewModel для каждого элемента UI, который вы хотите изменить программно.
Давайте также установим свойство для нашей модели и изменим свойства каждый раз, когда наша модель изменяется. Мы сделаем это путем объединения ‘старомодного’ способа (Наблюдатели свойства Swift) с помощью Rx. Мы отправим свойства объекта Weather в наш PublishSubjects, таким образом, они смогут сгенерировать значения в модели.
Примечание: Нам нужно убедиться, что это выполняется в основном потоке, так как метод onNext() выполняется в другом потоке! (метод onNext отправляет элемент Observer).
Теперь давайте присоединим нашу модель к свойству searchText, которое мы объявили выше. Мы сделаем это путем создания NSURLRequest каждый раз, когда в searchText вносятся изменения, и затем подпишем нашу модель на тот запрос. Мы сделаем это в методе init(), потому что мы знаем, что все наши свойства устанавливаются, когда вызывается метод init().
Таким образом, каждый раз, когда searchText изменяется, jsonRequest изменяет себя на соответствующий NSURLRequest. Каждый раз, когда он изменяется, наша модель получает данные которые мы получили от NSURLRequest.
Примечание: Метод rx_JSON() является фактически наблюдаемой последовательностью. Таким образом, jsonRequest — фактически Observable из Observable. Вот почему мы используем .switchLatest(), который заботиться о том, что jsonRequest возвращает только новую последовательность. Также имейте в виду, что запрос не начнет выборку, пока Вы не подпишитесь на него.
Теперь осталось только соеденить ViewController с ViewModel. Мы сделаем это за счет привязки PublishSubjects в ViewModel к аутлету в Controller.
Не забывайте, что мы также должны убедиться, что наш ViewModel знает то, что пользователь ввел в текстовом поле! Мы можем сделать это путем отправки значения nameTextField, каждый раз, когда оно изменяется, к searchText свойству ViewModel. Так что, мы просто добавим это в методе viewDidLoad ():

Вот так! Теперь приложение получает данные о погоде, в то время как пользователь вводит название города, и неважно, что пользователь видит, истинное состояние приложения остается скрытым.
Если Вы заинтересовались расширенной версией этого приложения, посмотрите мой пример приложения о погоде на Github.

ReactiveX — библиотека для создания асинхронных и основанных на событии программ при помощи наблюдаемой последовательности. — reactivex.io
RxSwift — относительно молодой фреймворк, который позволяет "реактивно программировать". Если Вы ничего о нем не знаете, тогда наведите справки, потому что функциональное реактивное программирование (FRP) набирает обороты, и не собирается останавливаться.
Итак, как же выглядит MVVM в iOS?
Способ соединения модели с ViewController при использований шаблона MVC часто выглядит, как некий хак. Вы, как правило, вызываете что-то вроде функции updateUI() в контроллере, когда думаете, что модель изменилась Это может привести к появлению несоответствий между моделью и ViewController, ненужным обновлениям и непонятным ошибкам.
Нам нужен ViewController, который покажет истинное состояние модели в любом случае. По сути, нам нужен ViewController, который будет являться простым прокси-сервером, который отобразит на экране данные согласно текущему состоянию модели.
Конечно, в большинстве приложений будут бесполезно, если они только отображают на экране модель. Нам необходимо получить данные из модели и подготовить их для отображения. Именно поэтому мы представляем класс ViewModel. ViewModel готовит все данные, которые необходимо отобразить на экране.
Но вот самое интересное: ViewModel ничего не знает о ViewController. Он никогда не ссылается на него и не имеет свойства напрямую. Вместо этого ViewController постоянно следит за любыми изменений ViewModel, и как только изменения произойдут, они сразу будут отображены на экране. Следует иметь в виду, что ViewController отображает на экране каждое свойство с ViewModel индивидуально, т.е. если вы хотите последовательно загрузить строку и изображение, вы сможете отобразить строку сразу посколько данные уже есть, и вам не придется ждать, пока изображение загрузится, чтобы отобразить их вместе.
Однако, ViewController отображает на экране не только данные, он также получает ввод данных от пользователя. Так как наш ViewController — просто прокси-сервер, он не может использовать эти данные, поэтому все, что он должен сделать, это передать их в ViewModel, и ViewModel сделает все остальное.

Это, в некотором смысле, односторонняя связь между ViewController и ViewModel. ViewController может видеть и обращаться к ViewModel, но у ViewModel нет ни малейшего представления о том, кто такой ViewController. Это значит, что Вы можете полностью удалить ViewController из своего приложения, и вся ваша логика будет работать так, как задумано!
Все это звучит прекрасно, но как мы это сделаем?
MVVM в связке с RxSwift
Давайте сделаем простое приложение, которое отображает прогноз погоды в городе, который вводит пользователь.
Эта статья предполагает, что вы знакомы с RxSwift. Если Вы ничего не знаете о нем, то смело читайте дальше, но я предлагаю больше узнать о ReactiveX.

У нас есть UITextField для ввода названия города и парочка UILabels, чтобы вывести на экран текущую температуру.
Примечание: для этого приложения я буду получать данные о погоде из OpenWeatherMap.
Так, наша модель будет классом Weather с несколькими свойствами name и degrees и инициализатором, который принимает объект JSON, который он анализирует и устанавливает свойства.
class Weather {
var name:String?
var degrees:Double?
init(json: AnyObject) {
let data = JSON(json)
self.name = data["name"].stringValue
self.degrees = data["main"]["temp"].doubleValue
}
}
Примечание: SwiftyJSON является обязательным для разбора JSON в Swift.
Теперь мы должны позволить ViewModel управлять моделью посредством общедоступного (public) свойства searchText, к которому у ViewController позже будет доступ.
class ViewModel {
private struct Constants {
static let URLPrefix = "http://api.openweathermap.org/data/2.5/weather?q="
static let URLPostfix = "/* my openweathermap APPID */"
}
let disposeBag = DisposeBag()
var searchText = PublishSubject<String?>()
Наш searchText является объектом PublishSubject. Subject’ы одновременно являются и Observable и Observer. Другими словами, вы можете отправить им элементы, которые они могут повторно сгенерировать.
PublishSubjects уникальны, потому что когда данные передаются в PublishSubject, он рассылает их всем подписчикам, которые подписаны на него в данный момент. Нам нужно это, потому что в MVVM, в зависимости от жизненного цикла приложения, Observable в различных классах иногда могут получать элементы, прежде чем вы подпишетесь на них. Как только ViewController подписывается на свойство ViewModel, он должен увидеть, что с последним элементом все в порядке, чтобы вывести его на экран, и наоборот.
Теперь мы должны объявить свойство в ViewModel для каждого элемента UI, который вы хотите изменить программно.
var cityName = PublishSubject<String>()
var degrees = PublishSubject<String>()
Давайте также установим свойство для нашей модели и изменим свойства каждый раз, когда наша модель изменяется. Мы сделаем это путем объединения ‘старомодного’ способа (Наблюдатели свойства Swift) с помощью Rx. Мы отправим свойства объекта Weather в наш PublishSubjects, таким образом, они смогут сгенерировать значения в модели.
var weather:Weather? {
didSet {
if let name = weather?.name {
dispatch_async(dispatch_get_main_queue()) {
self.cityName.onNext(name)
}
}
if let temp = weather?.degrees {
dispatch_async(dispatch_get_main_queue()) {
self.degrees.onNext("\(temp)°F")
}
}
}
}
Примечание: Нам нужно убедиться, что это выполняется в основном потоке, так как метод onNext() выполняется в другом потоке! (метод onNext отправляет элемент Observer).
Теперь давайте присоединим нашу модель к свойству searchText, которое мы объявили выше. Мы сделаем это путем создания NSURLRequest каждый раз, когда в searchText вносятся изменения, и затем подпишем нашу модель на тот запрос. Мы сделаем это в методе init(), потому что мы знаем, что все наши свойства устанавливаются, когда вызывается метод init().
init() {
let jsonRequest = searchText
.map { text in
return NSURLSession.sharedSession().rx_JSON(self.getURLForString(text)!)
}
.switchLatest()
jsonRequest
.subscribeNext { json in
self.weather = Weather(json: json)
}
.addDisposableTo(disposeBag)
}
Таким образом, каждый раз, когда searchText изменяется, jsonRequest изменяет себя на соответствующий NSURLRequest. Каждый раз, когда он изменяется, наша модель получает данные которые мы получили от NSURLRequest.
Примечание: Метод rx_JSON() является фактически наблюдаемой последовательностью. Таким образом, jsonRequest — фактически Observable из Observable. Вот почему мы используем .switchLatest(), который заботиться о том, что jsonRequest возвращает только новую последовательность. Также имейте в виду, что запрос не начнет выборку, пока Вы не подпишитесь на него.
Теперь осталось только соеденить ViewController с ViewModel. Мы сделаем это за счет привязки PublishSubjects в ViewModel к аутлету в Controller.
class ViewController: UIViewController {
let viewModel = ViewModel()
let disposeBag = DisposeBag()
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var degreesLabel: UILabel!
@IBOutlet weak var cityNameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//Binding the UI
viewModel.cityName.bindTo(cityNameLabel.rx_text)
.addDisposableTo(disposeBag)
viewModel.degrees.bindTo(degreesLabel.rx_text)
.addDisposableTo(disposeBag)
}
}
Не забывайте, что мы также должны убедиться, что наш ViewModel знает то, что пользователь ввел в текстовом поле! Мы можем сделать это путем отправки значения nameTextField, каждый раз, когда оно изменяется, к searchText свойству ViewModel. Так что, мы просто добавим это в методе viewDidLoad ():
nameTextField.rx_text.subscribeNext { text in
self.viewModel.searchText.onNext(text)
}
.addDisposableTo(disposeBag)

Вот так! Теперь приложение получает данные о погоде, в то время как пользователь вводит название города, и неважно, что пользователь видит, истинное состояние приложения остается скрытым.
Если Вы заинтересовались расширенной версией этого приложения, посмотрите мой пример приложения о погоде на Github.