Reactive Cocoa/Reactive Swift - Swift 3.0 Missing Methods

Reactive Cocoa / Reactive Swift - Swift 3.0 missing methods

Some parts of the Obj-C API have been divided in another framework : ReactiveObjC.

I needed to install this framework to access these methods.

Solution :

As stated in README (Objective-C and Swift section), those Objective-C
API are splitted out to ReactiveObjC framework. You need to add
https://github.com/ReactiveCocoa/ReactiveObjC as a submodule, link the
framework, then import ReactiveObjC.

Please see the following discussion on the issue :

https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3197

RACObserve doesn't give a value. (Reactive Cocoa with Swift)

According to Apple Documentation

When you assign a default value to a stored property, or set its initial value within an initializer, the value of that property is set directly, without calling any property observers.

Reactive observers work on KVO. I guess that is the reason why your observer is not getting called.

How to make basic bindings in ReactiveCocoa 3 and 4

So, after writing this question Colin Eberhardt made a part 3 of his RAC3 blog post series which includes a interesting and very relevant example of using MVVM and RAC3. The post can be found here and the source code here.

Based on his work, I've managed to answer my own questions:

  1. By taking a slightly different approach, I'm able to make the fetchTodaysMenu() return a SignalProducer<Menu, NSError> as wanted. Here's how what I then would do in my view model:

    MenuService.fetchTodaysMenu()
    |> observeOn(QueueScheduler.mainQueueScheduler)
    |> start(next: { response in
    self.mainCourse.put(response.mainCourse!)
    }, error: {
    println("Error \($0)")
    })
  2. It seems like there's no UIKit bindings yet as of RAC3 beta 4. Colin made some UIKit extensions himself to help him make these bindings I was looking for as well. These can be found here. Adding them to my project, made be able to do exactly what I wanted to:

    self.mainCourse.rac_text <~ self.viewModel.mainCourse

Update May 25, 2015

After been working a lot more with ReactiveCocoa 3, I would like to answer 1) once again. By using catch, it's possible to do this in a more declarative manner. I ended up implementing a small helper function for this:

public func ignoreError<T: Any, E: ErrorType>(signalProducer: SignalProducer<T, E>) -> SignalProducer<T, NoError> {
return signalProducer
|> catch { _ in
SignalProducer<T, NoError>.empty
}
}

The function transforms any NSError to NoError making it possible to bind as I wanted to by doing MenuService.fetchTodaysMenu() |> ignoreError.

I open sourced my project as this might be a good starting point for others looking into ReactiveCocoa 3.0:
https://github.com/s0mmer/TodaysReactiveMenu

Update March 5, 2016

As highlighted in the comments, since Swift 2, the ignoreError function would now look like:

public func ignoreError() -> SignalProducer<Value, NoError> {
return flatMapError { _ in
SignalProducer<Value, NoError>.empty
}
}

Also, an extension library called Rex has also been made, where something similar has been added.

How to combine three Signals using reactive cocoa in swift?

You can use Signal.merge for that, but first you have to convince the Compiler that you actually want to merge those different types

let p1 = MutableProperty<Int>(1)
let p2 = MutableProperty<Bool>(false)
let p3 = MutableProperty<String>("")

let s1: Signal<Any, NoError> = p1.signal.map { $0 }
let s2: Signal<Any, NoError> = p2.signal.map { $0 }
let s3: Signal<Any, NoError> = p3.signal.map { $0 }

let merged = Signal<Any, NoError>.merge(s1, s2, s3)
merged.observeValues { print($0) }

p1.value = 2
p1.value = 3
p3.value = "Hello"
p2.value = true

Without the map, the compiler will complain

error: cannot convert value of type 'Signal<Int, NoError>' to specified type 'Signal<Any, NoError>'

Form validation in reactive cocoa

Here's an example implementation of a basic ViewController, I've cleaned up a few of the things that I think could be done better.

class ViewController: UIViewController {
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!

func nameValidation(for field: UITextField) -> Signal<Bool, NoError> {
return field
.reactive
.continuousTextValues
.skipNil()
.map { $0.characters.count > 3 }
}

override func viewDidLoad() {
super.viewDidLoad()

let validUserNameSignal = nameValidation(for: nameTextField)
let lastNameFieldSignal = nameValidation(for: lastNameTextField)
let formValidation =
SignalProducer(signal: validUserNameSignal.combineLatest(with: lastNameFieldSignal))
.map { $0 && $1 }
.prefix(value: false)

formValidation.startWithValues {
print($0)
}
}
}

How to use rac_signalForControlEvents

I'm not sure how old the version you're using is, but at some point the library was split into two separate modules:

  1. ReactiveSwift for the general reactive primitives and operators.
  2. ReactiveCocoa for reactive integration with Cocoa.

So first make sure you have both of those libraries in your project. Then you can create a control event signal like this:

cell.addButton.reactive.controlEvents(.touchUpInside)
.observeValues { _ in
// Handle the button press here
}


Related Topics



Leave a reply



Submit