In your GitHub thread about this you say that you want to consider the remote's updates as canonical. That's good, because (as Josh Abernathy suggested there), RAC or not, you need to pick one of the two sources to take priority (or you need timestamps, but then you need a reference clock...).
Given your code and disregarding RAC, the solution is just setting a flag in seekToPosition:
and unsetting it using a timer. Check the flag in recievedPlayerUpdate:
, ignoring the update if it's set.
By the way, you should use the RAC()
macro to bind your slider's value, rather than the subscribeNext:
that you've got:
RAC(slider, value) = RACObserve(playerModel, position);
You can definitely construct a signal chain to do what you want, though. You've got four signals you need to combine.
对于最后一项,定期更新,您可以使用interval:onScheduler:
:
[[RACSignal interval:kPositionFetchSeconds
onScheduler:[RACScheduler scheduler]] map:^(id _){
return /* Request position over network */;
}];
map:
只是忽略信号产生的日期,interval:...
并获取位置。由于来自桌面的请求和消息具有相同的优先级,因此merge:
它们一起:
[RACSignal merge:@[desktopPositionSignal, timedRequestSignal]];
但是,如果用户触摸了滑块,您决定不希望这些信号中的任何一个通过。这可以通过两种方式之一来完成。使用我建议的标志,您可以filter:
合并信号:
[mergedSignal filter:^BOOL (id _){ return userFiddlingWithSlider; }];
比这更好 - 避免额外的状态 - 将构建一个操作 和 的组合,throttle:
在sample:
另一个信号没有发送任何东西之后,在某个时间间隔从一个信号传递一个值:
[mergedSignal sample:
[sliderSignal throttle:kUserFiddlingWithSliderInterval]];
(And you might, of course, want to throttle/sample the interval:onScheduler:
signal in the same way -- before the merge -- in order to avoid unncessary network requests.)
You can put this all together in PlayerModel
, binding it to position
. You'll just need to give the PlayerModel
the slider's rac_signalForControlEvents:
, and then merge in the slider value. Since you're using the same signal multiple places in one chain, I believe that you want to "multicast" it.
Finally, use startWith:
to get your first item above, the inital position from the desktop app, into the stream.
RAC(self, position) =
[[RACSignal merge:@[sampledSignal,
[sliderSignal map:^id(UISlider * slider){
return [slider value];
}]]
] startWith:/* Request position over network */];
The decision to break each signal out into its own variable or string them all together Lisp-style I'll leave to you.
Incidentally, I've found it helpful to actually draw out the signal chains when working on problems like this. I made a quick diagram for your scenario. It helps with thinking of the signals as entities in their own right, as opposed to worrying about the values that they carry.