Search code examples
swift3reactive-cocoa-5reactive-swift

Simple code swift to reactiveswift


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.


Solution

  • 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!