Search code examples
iosswiftreactive-programmingreactive-cocoa

Proper ReactiveCocoa Chaining


I have created two signal: getConnection and connection.rac_delete(). connection.rac_delete() depends on getConnection completing successfully.

What would be the ReactiveCocoa way of doing this? I currently have this solution but it doesn't feel like the right way.

getConnection().subscribeNext({
  let connection = $0 as! Connection

  connection.rac_delete().subscribeNext({ success in
    println("DELETED!")
  }, error: { error in
    println("ERROR DELETING!")
  })

}, error: { error in
  println("ERROR GETTING!")
})

Solution

  • So you have a signal of connections and you want to turn its values into something else (deletions).

    Normally you map signals to get new signals, but here you're mapping into another signal -- map would give you a signal of signals at this point.

    But you don't really want a signal of signals, because then you have to do this annoying nested subscription business on the result:

    // (pseudocode)
    getConnection()
    .map(connection -> connection.rac_delete())
    .subscribeNext(deletionSignal ->
      deletionSignal.subscribeCompleted(->
        println("done deleting")))
    

    That's no better than your current nested subscription -- you would like to simply flatten the signal of deletion signals into a signal of deletions, and subscribe to that directly. And that's exactly what flattenMap does!

    // (pseudocode)
    getConnection()
    .flattenMap(connection -> connection.rac_delete())
    .subscribeCompleted(->
      println("done deleting!"));
    

    Note, though, that this behaves differently than the above code, but only when getConnection()'s signal sends more than one value. Previously, it would log for each connection that finished deleting; now it will log only once at the end when all deletions complete.

    Since I assume the signal that getConnection() returns only sends one value, they'll probably behave the same in practice, but it's worth being aware of.

    I'm using subscribeCompleted here instead of subscribeNext because deletion seems like something that shouldn't really resolve to a value; it's just something that takes time. But that's easy to change.