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

Move more methods into <RACStream> #135

Merged
merged 25 commits into from Nov 28, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b3f439a
Moved -mapReplace: into <RACStream>
jspahrsummers Nov 25, 2012
0ae7ce1
Moved -injectObjectWeakly: into <RACStream>
jspahrsummers Nov 25, 2012
db101cc
Moved -scanWithStart:combine: into <RACStream>
jspahrsummers Nov 25, 2012
5af79e5
Moved -takeUntilBlock: and -takeWhileBlock: into <RACStream>
jspahrsummers Nov 25, 2012
6e3c0ee
Additional termination testing for -bind: on infinite streams
jspahrsummers Nov 26, 2012
dda82ae
Manually kill the disposable when -bind: terminates
jspahrsummers Nov 26, 2012
d24b995
Workaround for #94 specifically for the case of -bind:
jspahrsummers Nov 26, 2012
a212de4
Document the workaround
jspahrsummers Nov 26, 2012
8897a75
Moved -skipWhileBlock: and -skipUntilBlock: into <RACStream>
jspahrsummers Nov 26, 2012
6104c9c
Moved -distinctUntilChanged into <RACStream> as -skipRepeats
jspahrsummers Nov 26, 2012
f3c3acc
Merge branch 'master' into more-racstream
jspahrsummers Nov 26, 2012
6c82f0d
Merge branch 'master' into more-racstream
jspahrsummers Nov 26, 2012
92a6850
Implement -[RACSignal bind:] from scratch, without -flatten:
jspahrsummers Nov 27, 2012
6c4f9a9
Document expected -bind: semantics
jspahrsummers Nov 27, 2012
e0e656c
Remove unnecessary __block qualifier
jspahrsummers Nov 27, 2012
3d01525
Added some additional memory management unit tests
jspahrsummers Nov 27, 2012
adf7b70
Add a memory management test to RACPropertySignalExamples
jspahrsummers Nov 27, 2012
741670b
Merge branch 'bind-fixes' into more-racstream
jspahrsummers Nov 27, 2012
4502950
Update added methods to use the new -bind: interface
jspahrsummers Nov 27, 2012
c90cc49
Merge branch 'bind-fixes' into more-racstream
jspahrsummers Nov 28, 2012
44c05e4
Revert "Moved -distinctUntilChanged into <RACStream> as -skipRepeats"
jspahrsummers Nov 28, 2012
439a4bf
Explicitly compare against nil
jspahrsummers Nov 28, 2012
7691e10
Merge branch 'master' into more-racstream
jspahrsummers Nov 28, 2012
8a57788
Just remove -injectObjectWeakly:
jspahrsummers Nov 28, 2012
48a4c53
Refactor GHAPIDemo to not use -injectObjectWeakly:
jspahrsummers Nov 28, 2012
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 25 additions & 24 deletions GHAPIDemo/GHAPIDemo/GHDLoginViewController.m
Expand Up @@ -8,6 +8,7 @@

#import "GHDLoginViewController.h"
#import "EXTKeyPathCoding.h"
#import "EXTScope.h"
#import "GHDLoginView.h"
#import "GHGitHubClient.h"
#import "GHGitHubUser.h"
Expand Down Expand Up @@ -49,16 +50,16 @@ - (id)init {
}]
block:NULL];

[[self.loginCommand
injectObjectWeakly:self]
subscribeNext:^(RACTuple *t) {
GHDLoginViewController *self = t.last;
self.user = [GHGitHubUser userWithUsername:self.username password:self.password];
self.client = [GHGitHubClient clientForUser:self.user];
self.loggingIn = YES;
}];
@unsafeify(self);

[self.loginCommand subscribeNext:^(id _) {
@strongify(self);

self.user = [GHGitHubUser userWithUsername:self.username password:self.password];
self.client = [GHGitHubClient clientForUser:self.user];
self.loggingIn = YES;
}];

__block __unsafe_unretained id weakSelf = self;
// Note the -repeat and -asMaybes at the end. -repeat means that this
// Signal will resubscribe to its source right after it completes.
// This lets us subscribe to the same Signal even though the source
Expand All @@ -67,35 +68,35 @@ - (id)init {
// API hits an error, the Signal will still be valid.
RACSignal *loginResult = [[[self.loginCommand
addAsyncBlock:^(id _) {
GHDLoginViewController *strongSelf = weakSelf;
return [strongSelf.client login];
@strongify(self);
return [self.client login];
}]
asMaybes]
repeat];

// Since we used -asMaybes above, we'll need to filter out the specific
// error or success cases.
[[[[loginResult
[[[loginResult
filter:^(id x) {
return [x hasError];
}]
map:^(id x) {
return [x error];
}]
injectObjectWeakly:self]
subscribeNext:^(RACTuple *t) {
GHDLoginViewController *self = t.last;
subscribeNext:^(NSError *error) {
@strongify(self);

self.loginFailedHidden = NO;
NSLog(@"error logging in: %@", t.first);
NSLog(@"error logging in: %@", error);
}];

[[[loginResult
[[loginResult
filter:^(id x) {
return [x hasObject];
}]
injectObjectWeakly:self]
subscribeNext:^(RACTuple *t) {
GHDLoginViewController *self = t.last;
subscribeNext:^(id _) {
@strongify(self);

self.successHidden = NO;
[self.didLoginSubject sendNext:self.user];
}];
Expand All @@ -108,11 +109,11 @@ - (id)init {

// When either username or password change, hide the success or failure
// message.
[[[RACSignal
[[RACSignal
combineLatest:@[ RACAble(self.username), RACAble(self.password)]]
injectObjectWeakly:self]
subscribeNext:^(RACTuple *t) {
GHDLoginViewController *self = t.last;
subscribeNext:^(id _) {
@strongify(self);

self.successHidden = self.loginFailedHidden = YES;
}];

Expand Down
47 changes: 24 additions & 23 deletions GHAPIDemo/GHAPIDemo/GHDUserViewController.m
Expand Up @@ -7,9 +7,10 @@
//

#import "GHDUserViewController.h"
#import "EXTScope.h"
#import "GHDUserView.h"
#import "GHGitHubUser.h"
#import "GHGitHubClient.h"
#import "GHGitHubUser.h"
#import "NSView+GHDExtensions.h"

@interface GHDUserViewController ()
Expand Down Expand Up @@ -57,16 +58,17 @@ - (id)initWithUserAccount:(GHGitHubUser *)user {
self.client = [GHGitHubClient clientForUser:self.userAccount];
self.loading = YES;

__block __unsafe_unretained id weakSelf = self;
@unsafeify(self);

// We're using -merge: so that -fetchUser, -fetchRepos, and -fetchOrgs are
// all executed independently. We're then told when they've all completed.
// -finally: lets us share logic regardless of whether we get an error or
// complete successfully.
[[[RACSignal
merge:[NSArray arrayWithObjects:[self fetchUser], [self fetchRepos], [self fetchOrgs], nil]]
finally:^{
GHDUserViewController *strongSelf = weakSelf;
strongSelf.loading = NO;
@strongify(self);
self.loading = NO;
}]
subscribeNext:^(id x) {
// nothing
Expand All @@ -79,15 +81,15 @@ - (id)initWithUserAccount:(GHGitHubUser *)user {
// We're using -deliverOn: to load the image in a background queue and then
// finish with another -deliverOn: so that subscribers get the result on the
// main queue.
RACSignal *loadedAvatar = [[[[[RACAble(self.userAccount.avatarURL)
RACSignal *loadedAvatar = [[[[RACAble(self.userAccount.avatarURL)
filter:^ BOOL (id x) {
return x != nil;
}]
deliverOn:[RACScheduler sharedOperationQueueScheduler]]
injectObjectWeakly:self]
flattenMap:^(RACTuple *t) {
GHDUserViewController *self = t.last;
return [self loadImageAtURL:t.first];
flattenMap:^(NSURL *URL) {
@strongify(self);

return [self loadImageAtURL:URL];
}]
deliverOn:[RACScheduler mainQueueScheduler]];

Expand All @@ -103,32 +105,31 @@ - (id)initWithUserAccount:(GHGitHubUser *)user {
}

- (RACSignal *)fetchUser {
return [[[self.client
@unsafeify(self);
return [[self.client
fetchUserInfo]
injectObjectWeakly:self]
map:^(RACTuple *t) {
GHDUserViewController *self = t.last;
[self.userAccount setValuesForKeysWithDictionary:t.first];
map:^(NSDictionary *userDict) {
@strongify(self);

[self.userAccount setValuesForKeysWithDictionary:userDict];
return [RACUnit defaultUnit];
}];
}

- (RACSignal *)fetchRepos {
return [[[self.client
return [[self.client
fetchUserRepos]
injectObjectWeakly:self]
map:^(RACTuple *t) {
NSLog(@"repos: %@", t.first);
map:^(NSArray *repos) {
NSLog(@"repos: %@", repos);
return [RACUnit defaultUnit];
}];
}

- (RACSignal *)fetchOrgs {
return [[[self.client
fetchUserRepos]
injectObjectWeakly:self]
map:^(RACTuple *t) {
NSLog(@"orgs: %@", t.first);
return [[self.client
fetchUserOrgs]
map:^(NSArray *orgs) {
NSLog(@"orgs: %@", orgs);
return [RACUnit defaultUnit];
}];
}
Expand Down
35 changes: 0 additions & 35 deletions ReactiveCocoaFramework/ReactiveCocoa/RACSignalProtocol.h
Expand Up @@ -86,24 +86,6 @@ typedef NSInteger RACSignalError;
// Convenience method to subscribe to `error` and `completed` events.
- (RACDisposable *)subscribeError:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock;

// For each value sent on the receiving signal, the given object is sent on the
// returned signal.
//
// object - The object to send for each value sent on the receiver.
//
// Returns a signal that sends the given object for each value sent on the
// receiver.
- (id<RACSignal>)mapReplace:(id)object;

// Injects the given object weakly into the receiver's stream. The returned
// signal sends a tuple where the first object is the value received by the
// receiver signal and the second is the weak object.
//
// This is most useful for bringing the caller's self into the signal while
// preventing retain cycles so we don't always have to do the
// weakObject / strongObject dance.
- (id<RACSignal>)injectObjectWeakly:(id)object;

// Do the given block on `next`. This should be used to inject side effects into
// the signal.
- (id<RACSignal>)doNext:(void (^)(id x))block;
Expand Down Expand Up @@ -197,11 +179,6 @@ typedef NSInteger RACSignalError;
// block is called to get a new start object for each subscription.
- (id<RACSignal>)aggregateWithStartFactory:(id (^)(void))startFactory combine:(id (^)(id running, id next))combineBlock;

// Similar to -aggregateWithStart:combine: with an important difference: it
// sends the combined value with each `next` instead of waiting for the
// receiving signal to complete.
- (id<RACSignal>)scanWithStart:(id)start combine:(id (^)(id running, id next))combineBlock;

// Set the object's keyPath to the value of `next`.
- (RACDisposable *)toProperty:(NSString *)keyPath onObject:(NSObject *)object;

Expand All @@ -215,12 +192,6 @@ typedef NSInteger RACSignalError;
// Take `next`s until the `signalTrigger` sends a `next`.
- (id<RACSignal>)takeUntil:(id<RACSignal>)signalTrigger;

// Take `next`s until the given block returns YES.
- (id<RACSignal>)takeUntilBlock:(BOOL (^)(id x))predicate;

// Take `next`s until the given block returns NO.
- (id<RACSignal>)takeWhileBlock:(BOOL (^)(id x))predicate;

// Convert every `next` and `error` into a RACMaybe.
- (id<RACSignal>)asMaybes;

Expand All @@ -244,12 +215,6 @@ typedef NSInteger RACSignalError;
// Both success and error may be NULL.
- (id)firstOrDefault:(id)defaultValue success:(BOOL *)success error:(NSError **)error;

// Skips values until the block returns YES.
- (id<RACSignal>)skipUntilBlock:(BOOL (^)(id x))block;

// Skips values until the block returns NO.
- (id<RACSignal>)skipWhileBlock:(BOOL (^)(id x))block;

// Defer creation of a signal until the signal's actually subscribed to.
//
// This can be used to effectively turn a hot signal into a cold signal.
Expand Down
99 changes: 0 additions & 99 deletions ReactiveCocoaFramework/ReactiveCocoa/RACSignalProtocol.m
Expand Up @@ -117,26 +117,6 @@ - (RACDisposable *)subscribeError:(void (^)(NSError *))errorBlock completed:(voi
return [self subscribe:o];
}

- (id<RACSignal>)mapReplace:(id)object {
return [self map:^(id _) {
return object;
}];
}

- (id<RACSignal>)injectObjectWeakly:(id)object {
__unsafe_unretained id weakObject = object;
return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [self subscribeNext:^(id x) {
id strongObject = weakObject;
[subscriber sendNext:[RACTuple tupleWithObjectsFromArray:[NSArray arrayWithObjects:x ? : [RACTupleNil tupleNil], strongObject, nil]]];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[subscriber sendCompleted];
}];
}];
}

- (id<RACSignal>)doNext:(void (^)(id x))block {
NSParameterAssert(block != NULL);

Expand Down Expand Up @@ -670,23 +650,6 @@ - (RACDisposable *)subscribeError:(void (^)(NSError *))errorBlock completed:(voi
}];
}

- (id<RACSignal>)scanWithStart:(id)start combine:(id (^)(id running, id next))combineBlock {
NSParameterAssert(combineBlock != NULL);

return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
__block id runningValue = start;

return [self subscribeNext:^(id x) {
runningValue = combineBlock(runningValue, x);
[subscriber sendNext:runningValue];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[subscriber sendCompleted];
}];
}];
}

- (id<RACSignal>)aggregateWithStart:(id)start combine:(id (^)(id running, id next))combineBlock {
return [self aggregateWithStartFactory:^{
return start;
Expand Down Expand Up @@ -757,39 +720,6 @@ + (void)intervalTimerFired:(NSTimer *)timer {
}];
}

- (id<RACSignal>)takeUntilBlock:(BOOL (^)(id x))predicate {
NSParameterAssert(predicate != NULL);

return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
__block RACDisposable *selfDisposable = [self subscribeNext:^(id x) {
BOOL stop = predicate(x);
if(stop) {
[selfDisposable dispose], selfDisposable = nil;
[subscriber sendCompleted];
return;
}

[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[subscriber sendCompleted];
}];

return [RACDisposable disposableWithBlock:^{
[selfDisposable dispose];
}];
}];
}

- (id<RACSignal>)takeWhileBlock:(BOOL (^)(id x))predicate {
NSParameterAssert(predicate != NULL);

return [self takeUntilBlock:^BOOL(id x) {
return !predicate(x);
}];
}

- (id<RACSignal>)switch {
return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
__block RACDisposable *innerDisposable = nil;
Expand Down Expand Up @@ -890,35 +820,6 @@ - (id)firstOrDefault:(id)defaultValue success:(BOOL *)success error:(NSError **)
}];
}

- (id<RACSignal>)skipUntilBlock:(BOOL (^)(id x))block {
NSParameterAssert(block != NULL);

return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
__block BOOL keepSkipping = YES;
return [self subscribeNext:^(id x) {
if(keepSkipping) {
keepSkipping = !block(x);
}

if(!keepSkipping) {
[subscriber sendNext:x];
}
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[subscriber sendCompleted];
}];
}];
}

- (id<RACSignal>)skipWhileBlock:(BOOL (^)(id x))block {
NSParameterAssert(block != NULL);

return [self skipUntilBlock:^BOOL(id x) {
return !block(x);
}];
}

- (id<RACSignal>)distinctUntilChanged {
return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
__block id lastValue = nil;
Expand Down