I've been tasked with adding some instrumentation logic to an app to track the latency of various API calls. I'm struggling to come up with a clean, non side-effecting way to add timing instrumentation to methods that return RACSignal's (deferred execution API calls).
completed
eventThe only thing I've been able to come up with is to create a concrete RACSubscriber subclass that that handles the timer logic. Besides the nasty subclass, this obviously isn't ideal as it requires an explicit subscribe:
, which in turn requires a replay
on the source signal. Additionally, there is a burden on the caller as they have to at least refactor to get a temporary handle to the signal.
@interface SignalTimer : RACSubscriber
@property (nonatomic) NSDate *startDate;
@end
@implementation SignalTimer
- (void)didSubscribeWithDisposable:(RACDisposable *)disposable
{
[super didSubscribeWithDisposable:disposable];
self.startDate = [NSDate date];
}
- (void)sendCompleted
{
NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:self.startDate];
NSLog(@"Time elapsed: %f", duration);
[super sendCompleted];
}
@end
Usage would look like this:
- (RACSignal*)saveFoo:(Foo*)fooParseObj {
RACSignal *save = [[fooParseObj rac_save] replay]; // Don't forget replay!
[save subscribe:[[SignalTimer alloc] initWithName@"Saving the user's foo object"]];
return save;
}
Obviously, I'm not happy with this implementation.
Ideally, I'd like a chain-able method like this, but I wasn't sure how to accomplish it/if it was possible to handle a cold signal without nasty side-effects inside a category method (like calling replay
on the receiver).
[[[fooParseObj rac_save] logTimingsWithName:@"Saving the user's foo object"] subscribeNext:...];
Thoughts?
So I think I was making this way harder than it needed to be. The following category solution seems much more idiomatic, but I'd love any feedback.
@interface RACSignal (Timing)
- (instancetype)logTimingWithName:(NSString*)name;
@end
@implementation RACSignal (Timing)
- (instancetype)logTimingWithName:(NSString*)name
{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSDate* startDate = [NSDate date];
return [self subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:startDate];
NSLog(@"%@: %f sec", name, duration);
[subscriber sendCompleted];
}];
}];
}
@end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With