Pipe for repeating network requests in Reactive Cocoa 4 - swift

I want to refresh data every 15 seconds from an API using Reactive Cocoa 4. Since more than one subscriber can ask for this data at the same time, I want to have multiple subscribers to share one source of data.
My current approach is to have one Signal and share it to every instance that asks for the data. This Signal should start refreshing as soon as the first Signal is subscribed and end after the last has disposed.
SignalProducer<String, NoError> { observer, disposable in
self.disposable = self.repeatTimer.observeNext { _ in
NSLog("start network request")
observer.sendNext("result")
}
}.on(disposed: {
NSLog("disposed")
}).startWithSignal { signal, disposable1 in
self.updateSignal = signal
}
}
return (updateSignal, disposable!)
So for the first request I create and store the updateSignal and each following request will get that signal.
My first question: How can I know when the last subscriber disposed its signal? So when can I stop the requests?
My second question: I store the disposable from my repeatin network request in self.disposable which I also return to the subscriber. If the subscriber only disposes its Signal (which he got from Signal.observeNext()) the inner loop, where I log "start network request" is running endless. Do I really need to stop that Signal myself even when the outer Signal gets disposed?
Is there any nicer way or pattern for shared repeating requests?

Use the global timer function to perform work at specified intervals.
You could do something like this:
self.disposable =
timer(SomeTimeInterval onScheduler:QueueScheduler.mainQueueScheduler)
.startWithNext { _ in
//start network request here
}
But it's better if you chain your network request producer and observe the results, like this:
self.disposable =
timer(SomeTimeInterval onScheduler:QueueScheduler.mainQueueScheduler)
.flatMap(.Latest, transform { _ in
return self.networkRequestSignalProducer()
})
.start({ event in
//monitor the result of the network request
})
Note that you may not want to use the main queue like I did in this example, depending on how you've implemented your network requests.
If you want to avoid dealing with disposables, you can add a .takeUntil before .flatMap and terminate the timer with a signal

Related

How do I subscribe to multiple Observables individually and know when they are all finished?

I have come across a problem a few times now and I can't really figure out how to word it to get the answer I'm looking for on Google/SA so here goes.
I have multiple http observables. Lets say I want to delete object 1, 2 and 3. I send three http delete requests to myapi/{id} one after the other. I can subscribe to each request as each returns an Observable. This allows me to do something after each has completed or failed with myHttpObservable.subscribe(successFn, failFn).
Heres where the problem comes in. I now want to know when all three delete requests have finished. To do this I can use forkJoin([deleteRequest1, deleteRequest2, deleteRequest3] but forkJoin takes an array of Observables and subscribes to them. I cannot subscribe to them individually now since forkJoin wants an array or Observables.
So how do I subscribe to each Observable with its own success and failure functions but also know when all three are done?
The forkJoin will return the response of each observable in the same order your provided them. So for example, you have three observables
const source1$ = of("Observable 1");
const source2$ = of("Observable 2");
const source3$ = of("Observable 3");
You'll get the subscription of each in forkJoin like this
forkJoin([source1$, source2$, source3$]).subscribe(([res1, res2, res3]) => {
console.log(res1);
console.log(res2);
console.log(res3);
});
Here is the sample.
Thanks.

How to get number of requests waitinng to be processed when deployed a vertx httpserver as worker verticle?

Is there any other way to get a number of requests waiting to be processed by worker threads when deploying an HTTP server as worker verticle? I need an alternative for https://vertx.io/docs/vertx-dropwizard-metrics/java/#_pool_metrics.
You can try to utilize Asynchronous Counters which you can increment whenever you send an event to specific address on event bus and then decrement when verticle is done processing (or have just picked up an event). If you have lot of verticles and don't want to modify each of them, you can set outbound interceptor:
vertx.eventBus().addOutboundInterceptor(deliveryContext -> {
//you can validate if the address is what you are looking for
if (deliveryContext.message().address().equalsIgnoreCase("http event")) {
//increment counter
}
deliveryContext.next();
});
if you have a lot of addresses to cover you can always add some specific header to the message and then look for it in the interceptor but that would mean you have to modify each .send() call in worst case scenario:
vertx.eventBus().addOutboundInterceptor(deliveryContext -> {
//looking for specific header
if (deliveryContext.message().headers().contains("incrementCounterHeader")) {
//increment counter
}
deliveryContext.next();
});
//later in code
vertx.eventBus().send("http event", message,
new DeliveryOptions().addHeader("incrementCounterHeader", "somevalue"));
last but not least if you decide to use async counter you might want to propagate message only .onComplete() or .onSuccess(), depends on your business logic.
Hope this will help!

Swift Combine makeConnectable with PassthroughSubject

I'm having a hard time using the Connectable protocol on a PassthroughSubject. What I would like to do is be able to control when the PassthroughSubject begins sending events to its subscribers.
let eventPublisher = PassthroughSubject<String, Never>().makeConnectable()
let subscriber = MySubscriber()
eventPublisher.subscribe(subscriber)
eventPublisher.send("Hello") // Does not compile, send not found in Publisher.MakeConnectable
let cancelable = eventPublisher.connect()
// expect MySubscriber to recieve "Hello"
I'm new to Combine, but I don't understand how I can send values downstream to subscribers after becoming a connectable publisher and calling connect(). The docs are quite sparse on this topic and I'm hoping someone can show me how to achive this behavior.
You're close, but misunderstanding what the makeConnectable enables. It doesn't "queue up" data and prepare it for sending when it's available, it controls when the subscription is established.
As soon as the subscription is allowed (with .connect()), the publisher "is live" and a subscriber would receive a value that you send. However, anything you send before connect() is invoked is essentially dropped, not queued.
That said, if you invoke eventPublisher.send('after connect') after the last line, that will be received by the subscriber.
It sounds like what you want is a CurrentValueSubject, not a PassthroughSubject.
let countPublisher = CurrentValueSubject<Int,Never>(0)
var storage = Set<AnyCancellable>()
func f() {
self.countPublisher.value = 1
self.countPublisher
.sink {print($0)} // 1
.store(in:&self.storage)
}
As you can see, we can prime the pump with a stored value before any subscriber comes along, and when a subscriber does come along, it immediately receives the stored value. And after that, we can say .send and the subscriber(s) will receive whatever we send.

What's the right way to interrupt a long running Uber Cadence activity?

If I have a long-running activity that does something like
func Activity(ctx context.Context) error {
ticker := time.NewTicker(5 * time.Second)
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
if isServiceReady(ctx) {
break
}
}
}
return nil
}
and I'd like to be able to cancel that from a workflow (without the entire workflow getting cancelled) how would I then do that?
I had hoped that you could receive on the context Done channel and then create a cancellable context in the workflow, but that apparently doesn't do anything.
Currently to be cancellable an activity has to heartbeat. The Cadence service piggybacks the cancellation status on the activity heartbeat return value. So if you start heartbeating on every tick and your heartbeat interval is small enough (as client doesn't make call the service on every heartbeat method call) then your code should start working.
In the future we plan to add notion of a session with a worker specific task list to deliver cancellations. After it is added the cancellation wouldn't rely on heartbeats to be delivered.

RxSwift flatMap latest one time without disposing

Had a quick question: I have an Variable<[Session]>, where Session:
class Session {
...
var rx_serverRequestable: Driver<SessionRequestable>
...
}
which emits a .next event every time the session has all the information it needs to be passed on to the backend and I want to be able to flatMapLatest on the array of sessions, and do something like:
let sessions: Variable<[Session]>
sessions
.flatMapLatest { sessions in sessions.map { $0. rx_serverRequestable } }
.flatMap { $0.requestFromServer() }
but I only want to request each session once. There are two ways I see that failing with my current implementation:
1. flatMapLatest gets a new array of sessions, potentially disposing a request from server thats still in progress
2. rx_serverRequestable gets called each time the session has all the information required to be loaded from server, so it will get called multiple times, each time the session loads in any new information. I only want the session to be requested the first time, should I be using something like .multicast or .replay(1)?
any pointers on solving the two issues, or switching up my approach?
This can be solved using the zip operator http://reactivex.io/documentation/operators/zip.html