Skip to content

Instantly share code, notes, and snippets.

@jonnolen
Last active December 20, 2015 11:49
Show Gist options
  • Save jonnolen/6126788 to your computer and use it in GitHub Desktop.
Save jonnolen/6126788 to your computer and use it in GitHub Desktop.
Test cases to show issue with sendComplete for subjects on gcd queue.
#import <ReactiveCocoa/ReactiveCocoa.h>
@interface rac_signal_testTests: SenTestCase
@end
@implementation rac_signal_testTests
- (void)setUp
{
[super setUp];
// Set-up code here.
}
- (void)tearDown
{
// Tear-down code here.
[super tearDown];
}
-(void)test_merged_subjects_will_complete_on_main_thread{
RACSubject *subject1 = [[RACSubject subject] setNameWithFormat:@"subject1"];
RACSubject *subject2 = [[RACSubject subject] setNameWithFormat:@"subject2"];
RACSignal *merged = [RACSignal merge:@[subject1, subject2]];
__block BOOL completed_fired = NO;
[merged subscribeCompleted:^{
completed_fired = YES;
}];
[subject1 sendNext:@"1"];
[subject2 sendNext:@"2"];
[subject1 sendCompleted];
[subject2 sendCompleted];
STAssertTrue(completed_fired, nil);
}
//test proving that throttling isn't breaking the merged signal (initial hypothesis).
-(void)test_merged_subjects_will_complete_if_one_of_them_has_a_throttled_subscriber_on_main_thread{
RACSubject *subject1 = [[RACSubject subject] setNameWithFormat:@"subject1"];
RACSubject *subject2 = [[RACSubject subject] setNameWithFormat:@"subject2"];
__block NSString * hit_subject2_next = nil;
[[subject2 throttle:.5] subscribeNext:^(NSString *value){
hit_subject2_next = value;
}];
RACSignal *merged = [RACSignal merge:@[subject1, subject2]];
__block BOOL completed_fired = NO;
[merged subscribeCompleted:^{
completed_fired = YES;
}];
[subject2 sendNext:@"2"];
[subject2 sendCompleted];
[subject1 sendCompleted];
STAssertEqualObjects(@"2", hit_subject2_next, nil);
STAssertTrue(completed_fired, nil);
}
-(void)test_merged_subjects_will_complete_if_on_gcd_queue{
__block BOOL complete = NO;
dispatch_queue_t global_default_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(global_default_queue, ^{
RACSubject *subject1 = [[RACSubject subject] setNameWithFormat:@"subject1"];
RACSubject *subject2 = [[RACSubject subject] setNameWithFormat:@"subject2"];
__block NSString * hit_subject2_next = nil;
RACScheduler *global_default_scheduler = [RACScheduler schedulerWithQueue:global_default_queue name:@"com.test.global_default"];
RACSignal *sig1 = [subject1 deliverOn:RACScheduler.mainThreadScheduler];
RACSignal *sig2 = [subject2 deliverOn:RACScheduler.mainThreadScheduler];
[sig2 subscribeNext:^(NSString *value){
hit_subject2_next = value;
}];
[sig2 subscribeCompleted:^{
NSLog(@"hit sig2 complete");
}];
[sig1 subscribeCompleted:^{
NSLog(@"hit sig1 complete");
}];
RACSignal *merged = [[RACSignal merge:@[sig1, sig2]] deliverOn:RACScheduler.mainThreadScheduler];
[merged subscribeCompleted:^{
complete = YES;
}];
[subject2 sendNext:@"2"];
// if we dispatch the send complete calls to the main queue then this code works but that seems like it shoul be unnecessary.
// dispatch_async(dispatch_get_main_queue(), ^{
[subject1 sendCompleted];
[subject2 sendCompleted];
// });
});
NSDate *startTime = NSDate.date;
do{
[NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:.5]];
}while(!complete && [NSDate.date timeIntervalSinceDate:startTime] <= 10.0);
STAssertTrue(complete, nil);
}
@end
@jonnolen
Copy link
Author

The only way the test test_maybe_its_the_background_thread passes is if you dipsatch subject1 and subject2 send complete to the main queue.

Both of the completion blocks get hit if they are delivered on the main thread but the merged signal never completes.

This replicates and issue I'm having where a concurrent NSOperation exposes a RACSignal (internally it's a subject) that it triggers when it finishes.

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