New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mapping RAC2 hot signals to RAC3 Signal #1930

Closed
ColinEberhardt opened this Issue Apr 22, 2015 · 16 comments

Comments

Projects
None yet
7 participants
@ColinEberhardt
Copy link

ColinEberhardt commented Apr 22, 2015

I'm trying to map between RAC2 and RAC3 concepts, most of which seem to make sense. The one that I am struggling with is the mapping of hot signals.

The ObjC Bridging API allows you to construct a SignalProducer from a RACSignal, however it does not have an API method for constructing a Signal from a RACSignal.

As a result, any RAC2 signal is bridged to a SignalProducer and as such becomes a cold signal. Personally I consider signals derived from UI control to be hot, however I know that this is debatable.

This doesn't stop me getting the job done, I can certainly construct a SignalProducer in this case:

extension UITextField {
  func rac_textSignalProducer() -> SignalProducer<String, NSError> {
    return
      self.rac_textSignal().toSignalProducer()
        |> map { $0 as! String }
  }
}

And this works just fine for my purposes, but it does still leave me wondering how to bridge hot signals from RACSignal to Signal?

@michaelmcguire

This comment has been minimized.

Copy link

michaelmcguire commented Apr 22, 2015

I had a similar question. It seems like the signals from UI controls should be "hot" or a Signal but using that type limits some of the operators you can use. Perhaps the assumption that UI controls should be 'hot' is incorrect.

@ColinEberhardt

This comment has been minimized.

Copy link

ColinEberhardt commented Apr 23, 2015

Interesting, I've just read that thread. I'm glad that @neilpa is in agreement that signals emitted by UI Controls are 'hot' and hence should be modelled as a 'Signal'.

However, as you mentioned there are quite a few operations that can be applied to SignalProducers that I would naturally want to apply to signals.

For example on, (yeah, I know, side-effects are probably bad!), or flatMap, where I might want to trigged a network request based on some user input, where the UI controls signal is mapped to a signal producer that performs the network request (and the flattened).

I went down a similar route, implementing a few extra operations on Signal, but then switched to using SwignalProducer which has the whole 'vocabulary' available to it.

@neilpa

This comment has been minimized.

Copy link
Member

neilpa commented Apr 23, 2015

but it does still leave me wondering how to bridge hot signals from RACSignal to Signal?

You can't, but this should be less of an issue once the UIKit/AppKit extensions are updated to use the new APIs directly. That work hasn't happened yet because the new API was in-flux until recently.

For example on

The equivalent of this for hot Signals is to just observe events. The purpose of on is to attach side effects on a SignalProducer without starting the underlying work at the call-site.

or flatMap, where I might want to trigged a network request based on some user input

Generally you'll want to use an Action for this which is a lot better than the old RACCommand.

@ColinEberhardt

This comment has been minimized.

Copy link

ColinEberhardt commented Apr 25, 2015

Thanks for the feedback. I'm familiar with the general concept of commands. Although with the example I am considering I don't feel that they are appropriate.

As an example, I have a text field where I want to provide suggestions as the user types. This requires an API request.

To my mind, the text input becomes a signal, which is throttled then flatMapped with a signal producer that represents the API request.

Personally I don't feel that actions / commands are the correct model for a text field's text.

@neilpa

This comment has been minimized.

Copy link
Member

neilpa commented Apr 26, 2015

text input becomes a signal, which is throttled then flatMapped with a signal producer that represents the API request.

This is what I'm suggesting too where Action encapsulates the mapping from input to SignalProducer via its apply method. However, I now realize my suggestion has the exact problem you're describing. There's no way to promote the inner producer values into the outer signal's stream. To allow for this join would need to defined on signal-of-producers.

@OscarSwanros

This comment has been minimized.

Copy link

OscarSwanros commented Apr 30, 2015

@ColinEberhardt How would you convert the NSError param to a NoError one?

extension UITextField {
    func rac_textSignalProducer() -> SignalProducer<String, NoError> {
        return
            self.rac_textSignal().toSignalProducer()
                |> map { $0 as! String }
                |> mapError { $0 as! NoError }
    }
}

Which fails with Cast from 'NSError' to unrelated type 'NoError' always fails.

Any ideas on how to implement this? I know you can't instantiate a NoError, which leaves me wondering.

@ikesyo

This comment has been minimized.

Copy link
Member

ikesyo commented Apr 30, 2015

@OscarSwanros How about the following: |> catch { _ in SignalProducer<String, NoError>.empty }

@OscarSwanros

This comment has been minimized.

Copy link

OscarSwanros commented Apr 30, 2015

It did work, thanks!

@ColinEberhardt

This comment has been minimized.

Copy link

ColinEberhardt commented Apr 30, 2015

@OscarSwanros rather than cast to NoError cant you just return one regardless?

@OscarSwanros

This comment has been minimized.

Copy link

OscarSwanros commented Apr 30, 2015

Yes, that's what I ended up doing.

@jspahrsummers

This comment has been minimized.

Copy link
Member

jspahrsummers commented May 3, 2015

I like @neilpa's suggestion above:

To allow for this join would need to defined on signal-of-producers.

This doesn't really cause problems in the same way that join over signal-of-signals does, so I think we should offer it to help bridge the gap.

@jspahrsummers jspahrsummers added this to the 3.0 milestone May 3, 2015

@neilpa

This comment has been minimized.

Copy link
Member

neilpa commented May 4, 2015

This doesn't really cause problems in the same way that join over signal-of-signals does, so I think we should offer it to help bridge the gap.

And even for signal-of-signals, join is mostly a problem for Concat. Otherwise Merge and Latest can still behave sensibly if the inner signals have already completed.

@jspahrsummers

This comment has been minimized.

Copy link
Member

jspahrsummers commented May 5, 2015

@neilpa Since Signals send Interrupted if observed after they've already terminated, those operators wouldn't behave as expected.

@neilpa

This comment has been minimized.

Copy link
Member

neilpa commented May 5, 2015

I was thinking about pre-Interrupted world. However, this makes me wonder if an Interrupted event on the inner signals should be ignored rather than "poison" the outer signal in the same way as Error.

@neilpa

This comment has been minimized.

Copy link
Member

neilpa commented May 5, 2015

@jspahrsummers I opened #1967 to continue this discussion.

@jspahrsummers

This comment has been minimized.

Copy link
Member

jspahrsummers commented Jul 18, 2015

#1966 was reverted because of bugs. Gonna reopen this so we can track it as an enhancement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment