People how I can convert my code:
struct CarModel {
var model: String?
var make: String?
var kilowatts: Int?
var photoURL: String?
init(model: String, make: String, kilowatts: Int, photoURL: String) {
self.model = model
self.make = make
self.kilowatts = kilowatts
self.photoURL = photoURL
}
}
and:
class CarViewModel {
private var car: Car?
static let HPperKW = 1.34102209
var modelText: String? {
return car?.model
}
var makeText: String? {
return car?.make
}
var horsepowerText: String? {
guard let kilowatts = car?.kilowatts else { return nil }
let HP = Int(round(Double(kilowatts) * CarViewModel.HPperKW))
return "\(HP) HP"
}
var titleText: String? {
guard let make = car?.make, let model = car?.model else { return nil }
return "\(make) \(model)"
}
var photoURL: URL? {
guard let photoURL = car?.photoURL else { return nil }
return URL(string: photoURL)
}
init(_ car: Car) {
self.car = car
}
}
to ReactiveCocoa/ReactiveSwift. I read off. documentation about Reactive but I did not understand how I can implemented Reactive API to my code. Who knows how I need to do it, please tell me. And else one who knows good samples/examples/tutorials for last version ReactiveCocoa/ReactiveSwift, please tell me.
ReactiveCocoa is for binding dynamic data (held in your viewmodel) to the UI of a ViewController. If your data is not dynamic (if the viewmodel does not change over the course of the viewcontroller's lifetime) you do not need to use reactivecocoa at all. However, if your car
variable will change and a single viewcontroller will be used to display multiple cars, reactivecocoa will be very useful. You can use the MutableProperty
class to encapsulate the dynamic car
variable and create signals that will update the ViewController whenever the car property changes.
class CarViewModel {
let car: MutableProperty<Car>
init(_ car: Car) {
self.car = MutableProperty(car)
}
var modelTextSignal: SignalProducer<String, NoError> {
return car.producer.map { $0.model }
}
var makeTextSignal: SignalProducer<String, NoError> {
return car.producer.map { $0.make }
}
var horsepowerTextSignal: SignalProducer<String, NoError> {
return car.producer.map { car in
let HP = Int(round(Double(car.kilowatts) * CarViewModel.HPperKW))
return "\(HP) HP"
}
}
var titleTextSignal: SignalProducer<String, NoError> {
return car.producer.map { "\($0.make) \($0.model)" }
}
var photoURLSignal: SignalProducer<URL?, NoError> {
return car.producer.map { URL(string: $0.photoURL) }
}
}
now, we have a bunch of signals that represent the changing car
data over time, and can use ReactiveCocoa to bind these signals to the UI so that the UI automatically updates with the new car data every time viewModel.car.value
is updated!
class CarViewController: UIViewController {
@IBOutlet modelLabel: UILabel!
@IBOutlet makeLabel: UILabel!
@IBOutlet horsepowerLabel: UILabel!
@IBOutlet titleLabel: UILabel!
@IBOutlet image: UIImageView!
var viewModel: CarViewModel!
override func viewDidLoad() {
self.modelLabel.reactive.text <~ self.viewModel.modelTextSignal
self.makeLabel.reactive.text <~ self.viewModel.makeTextSignal
self.horsepowerLabel.reactive.text <~ self.viewModel.horsepowerTextSignal
self.titleLabel.reactive.text <~ self.viewModel.titleTextSignal
self.viewModel.photoURLSignal.startWithValues { [weak self] url in
self?.setImageFromUrl(url)
}
}
func displayNewCar() {
self.viewModel.car.value = aRandomCar()
}
private func setImageFromUrl(url: URL?) {
//download url and display in UIImageView
}
private func aRandomCar() -> Car {
//return a Car object
}
}
So you can see how if you only need to display a single unchanging car object in your view controller, ReactiveCocoa is unneeded-- however, if your viewmodel is changing throughout the course of the viewcontroller's lifetime, reactivecocoa will allow you to bind your mutable data to the UI so that your views automatically update whenever the data changes!