Skip to content
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

Substitution for SignalProducer.buffer() #3070

Closed
dev4dev opened this issue Jul 20, 2016 · 11 comments
Closed

Substitution for SignalProducer.buffer() #3070

dev4dev opened this issue Jul 20, 2016 · 11 comments
Labels

Comments

@dev4dev
Copy link

dev4dev commented Jul 20, 2016

Deprecation warning says nothing to me - Use properties instead. 'buffer' will be removed in RAC 5.0

What properties? Could someone provide clear example. Thanks.

@andersio
Copy link
Member

andersio commented Jul 20, 2016

  1. buffer(0)
    It is semantically the same as Signal.pipe, but less efficient.
  2. buffer(1)
    This is the most common use case AFAICT, and it is effectively a MutableProperty without the guarantee of always having a current value at compile time. That's why we recommend properties over buffer(1) in the deprecation message.
  3. buffer(n) for n > 1
    You may consider SignalProducer.replayLazily as the replacement, albeit the semantics being a bit different.

@dev4dev
Copy link
Author

dev4dev commented Jul 20, 2016

@andersio oh, thanks a lot, I thought not about right properties

@mdiep mdiep closed this as completed Jul 20, 2016
@benji-bou
Copy link

Hy,

What if I wanted to send back an error to the buffered SignalProducer?
In the MutableProperty there is NoError in the Signal or SignalProducer

Regards

@andersio
Copy link
Member

andersio commented Aug 10, 2016

If you really need the old buffer semantics, you may create a Signal pipe, wrap it with a SignalProducer and use replayLazily.

Note that replayLazily starts caching events only if it has ever been started.

let (signal, observer) = Signal<Int, TestError>.pipe()
let replayedProducer = SignalProducer(signal: signal).replayLazily(1)

// Start the buffering immediately.
replayedProducer.start()

@benji-bou
Copy link

Thanks, It's what I was looking for

@wdcew
Copy link

wdcew commented Sep 2, 2016

Thanks,

@roodkcab
Copy link

replayLazily seems has side effect.

take a look at example below

let (signal, observer) = Signal<Int, NSError>.pipe()
let replayedProducer = SignalProducer(signal: signal).replayLazily(1)

// Start the buffering immediately.
replayedProducer.start()

func someAsyncProcedure() {
    observer.send(error: NSError())                           //<------------       LINE 2
}

func someProcedure {
    replayedProducer.startWithResult {
        switch($0) {
        case .success(let value):
           print(value)
        case .failure(let error):
           print(error)                                        //<------------      LINE 1
        }
    }
    someAsyncProcedure()
}

someProcedure() //everything is fine
someProcedure() //LINE 1 is called before LINE 2

I didn't read the code of replayLazily, but it seems used a cached value instead of using observer sent value. Problem is when observer send value, the failure procedure is called instead of success.

To solve this problem I have to create a Signal every time I call someProcedure, don't think it's a good practice... any comment is welcome

@andersio
Copy link
Member

andersio commented Oct 29, 2016

When the second time you call someProcedure, the underlying producer has already terminated with the error you sent in the first time you call someProcedure (with someAsyncProcedure).

The replayed producer cached this error for you, and this is why you get "Line 1" called before "Line 2" - it replayed the error.

Please note that sending an error means termination of the event stream.

@roodkcab
Copy link

roodkcab commented Oct 30, 2016

@andersio Thanks for replying.

I guess since the producer is terminated, it's OK to create a new signal to handle the async procedure like this

class SomeClass {
    private var observer: Observer<String, NSError>!

    //delegate method async called from other thread
    func someAsyncProcedure() {
        observer.send(error: NSError())                           //<------------       LINE 2
    }

    func someProcedure -> SignalProducer<String, NSError> {
        let producer SignalProducer<String, NSError> { [weak self] sink, _ in
            self!.observer = sink
        }
        return producer
    }
}

func xxx() {
    SomeClass.someProcedure().startWithResult {
        switch($0) {
            case .success(let value):
                print(value)
            case .failure(let error):
                print(error)                                        //<------------      LINE 1
        }
    }
}

xxx()
xxx()

I guess what I want is Future semantics here...

I've read #2744 and noticed

PropertyType has the exact same semantics as SignalProducer.buffer

But as benji-bou said, Property does not emit errors. It seems I have to use two Property to handle error, one is Success Property, wrapping the value I want, the other is an Error Property, and I have to observe on both of them, right?

@andersio
Copy link
Member

andersio commented Oct 30, 2016

It seems what you want is wrapping a delegate pattern as streams of values, while the delegate is triggered as a result of starting the producer.

In this case, I think you'd be better off with an internal Signal<Result<Value, Error>, NoError> signal, and pipe your delegate callback results into it. Then you may derive producers from it.

let asyncResult: Signal<Result<Value, Error>, NoError>

func work() -> SignalProducer<Value, Error> { observer, disposable in
    // Mutual exclusion via semaphore or queues, if necessary.
    disposable += asyncResult.observeValues { result in
        switch result {
        case let .success(value):
            observer.send(value: value)
            observer.sendCompleted()
        case let .failure(error):
            observer.send(error: error)
        }
    }
    startTheAsyncWorkThatCallbacksViaDelegate()
}

@roodkcab
Copy link

@andersio Thanks for your patience, problem solved 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants