Permalink
Browse files

Add ReactiveCocoa

  • Loading branch information...
1 parent df24770 commit 4bd4bf89a51bc8ba19718b0de0bf15c0f43b4c7a @henrikhodne henrikhodne committed Nov 30, 2012
Showing with 154 additions and 63 deletions.
  1. +2 −1 Podfile
  2. +23 −1 Podfile.lock
  3. +14 −17 src/AppDelegate.m
  4. +7 −3 src/TravisAPI.h
  5. +19 −7 src/TravisAPI.m
  6. +4 −13 src/TravisEventFetcher.h
  7. +17 −3 src/TravisEventFetcher.m
  8. +18 −3 src/TravisHTTPClient.h
  9. +50 −15 src/TravisHTTPClient.m
View
3 Podfile
@@ -1,2 +1,3 @@
platform :osx, '10.7'
-pod 'AFNetworking', '~> 1.0'
+pod 'AFNetworking', '~> 1.0'
+pod 'ReactiveCocoa', '~> 0.10.0'
View
24 Podfile.lock
@@ -1,11 +1,33 @@
PODS:
- AFNetworking (1.0.1)
+- JRSwizzle (1.0)
+- ReactiveCocoa (0.10.0):
+ - ReactiveCocoa/Core (= 0.10.0)
+ - ReactiveCocoa/RACExtensions (= 0.10.0)
+- ReactiveCocoa/Core (0.10.0):
+ - JRSwizzle (~> 1.0)
+ - libextobjc/EXTConcreteProtocol (~> 0.2.3)
+ - libextobjc/EXTKeyPathCoding (~> 0.2.3)
+ - libextobjc/EXTScope (~> 0.2.3)
+- ReactiveCocoa/RACExtensions (0.10.0):
+ - ReactiveCocoa/Core
+- libextobjc/EXTConcreteProtocol (0.2.3)
+- libextobjc/EXTKeyPathCoding (0.2.3)
+- libextobjc/EXTScope (0.2.3)
DEPENDENCIES:
- AFNetworking (~> 1.0)
+- ReactiveCocoa (~> 0.10.0)
SPEC CHECKSUMS:
AFNetworking: 4c11f857c9a18d65f4257c90a1e74ac49f0ac422
+ JRSwizzle: 6242ff38485d870fc2636899048ee1ab883ae587
+ ReactiveCocoa: 1d0b1bca0092ca3088d7ae66799ce0116c25a612
+ ReactiveCocoa/Core: 1d0b1bca0092ca3088d7ae66799ce0116c25a612
+ ReactiveCocoa/RACExtensions: 1d0b1bca0092ca3088d7ae66799ce0116c25a612
+ libextobjc/EXTConcreteProtocol: 7dcd66faf065d3b7fcb5f85a1646efaed28bcadf
+ libextobjc/EXTKeyPathCoding: 7dcd66faf065d3b7fcb5f85a1646efaed28bcadf
+ libextobjc/EXTScope: 7dcd66faf065d3b7fcb5f85a1646efaed28bcadf
-COCOAPODS: 0.15.2
+COCOAPODS: 0.16.0
View
31 src/AppDelegate.m
@@ -15,8 +15,9 @@
#import "NotificationDisplayer.h"
#import "TravisAPI.h"
#import "RepositoryFilter.h"
+#import <ReactiveCocoa/ReactiveCocoa.h>
-@interface AppDelegate () <TravisEventFetcherDelegate, NSUserNotificationCenterDelegate>
+@interface AppDelegate () <NSUserNotificationCenterDelegate>
@property (strong) TravisEventFetcher *eventFetcher;
@property (strong) NSStatusItem *statusItem;
@@ -31,8 +32,18 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification {
[self setupGrowl];
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
- [self setEventFetcher:[[TravisEventFetcher alloc] init]];
- [[self eventFetcher] setDelegate:self];
+ [self setEventFetcher:[TravisEventFetcher eventFetcher]];
+ [[[[self eventFetcher] eventStream] filter:^(TravisEvent *event) {
+ return [self shouldShowNotificationFor:event];
+ }] subscribeNext:^(TravisEvent *event) {
+ [[[TravisAPI standardAPI] fetchBuildWithID:[event buildID] forRepository:[event name]] subscribeNext:^(NSDictionary *build) {
+ [event updateBuildInfo:build];
+ Notification *notification = [Notification notificationWithEventData:event];
+ [[NotificationDisplayer sharedNotificationDisplayer] deliverNotification:notification];
+ } error:^(NSError *error) {
+ NSLog(@"Couldn't get build info from JSON API. Error: %@.", error);
+ }];
+ }];
[self setupStatusBarItem];
}
@@ -77,20 +88,6 @@ - (BOOL)shouldShowNotificationFor:(TravisEvent *)eventData {
return [filter acceptsRepository:[eventData name]];
}
-#pragma mark - TravisEventFetcherDelegate
-
-- (void)eventFetcher:(TravisEventFetcher *)eventFetcher gotEvent:(TravisEvent *)event {
- if ([self shouldShowNotificationFor:event]) {
- [[TravisAPI new] getBuildWithID:[event buildID] forRepository:[event name] success:^(NSDictionary *build) {
- [event updateBuildInfo:build];
- Notification *notification = [Notification notificationWithEventData:event];
- [[NotificationDisplayer sharedNotificationDisplayer] deliverNotification:notification];
- } failure:^(NSError *error) {
- NSLog(@"Couldn't get build info from JSON API. Error: %@.", error);
- }];
- }
-}
-
#pragma mark - NSUserNotificationCenterDelegate
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification {
View
10 src/TravisAPI.h
@@ -8,11 +8,15 @@
#import <Foundation/Foundation.h>
-typedef void (^BuildResponse)(NSDictionary *build);
-typedef void (^FailureResponse)(NSError *error);
+@class TravisHTTPClient;
+@class RACSignal;
@interface TravisAPI : NSObject
-- (void)getBuildWithID:(NSNumber *)buildID forRepository:(NSString *)slug success:(BuildResponse)success failure:(FailureResponse)failure;
++ (TravisAPI *)standardAPI;
+
+- (id)initWithHTTPClient:(TravisHTTPClient *)HTTPClient;
+
+- (RACSignal *)fetchBuildWithID:(NSNumber *)buildID forRepository:(NSString *)slug;
@end
View
26 src/TravisAPI.m
@@ -9,16 +9,28 @@
#import "TravisAPI.h"
#import "TravisHTTPClient.h"
+@interface TravisAPI ()
+@property (nonatomic, strong) TravisHTTPClient *HTTPClient;
+@end
+
@implementation TravisAPI
-- (void)getBuildWithID:(NSNumber *)buildID forRepository:(NSString *)slug success:(BuildResponse)success failure:(FailureResponse)failure {
- NSString *path = [NSString stringWithFormat:@"/%@/builds/%@.json", slug, buildID];
++ (TravisAPI *)standardAPI {
+ return [[self alloc] initWithHTTPClient:[TravisHTTPClient standardHTTPClient]];
+}
+
+- (id)initWithHTTPClient:(TravisHTTPClient *)HTTPClient {
+ self = [super init];
+ if (self == nil) return nil;
- [[TravisHTTPClient sharedHTTPClient] getPath:path parameters:nil success:^(AFHTTPRequestOperation *operation, NSDictionary *build) {
- if (success) success(build);
- } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
- if (failure) failure(error);
- }];
+ _HTTPClient = HTTPClient;
+
+ return self;
+}
+
+- (RACSignal *)fetchBuildWithID:(NSNumber *)buildID forRepository:(NSString *)slug {
+ NSString *path = [NSString stringWithFormat:@"/%@/builds/%@.json", slug, buildID];
+ return [[self HTTPClient] requestWithMethod:TravisHTTPClientMethodGET path:path parameters:nil];
}
@end
View
17 src/TravisEventFetcher.h
@@ -7,19 +7,10 @@
//
#import <Cocoa/Cocoa.h>
-
-@protocol TravisEventFetcherDelegate;
-@class TravisEvent;
+@class RACSignal;
@interface TravisEventFetcher : NSObject
+@property (readonly, strong) RACSignal *eventStream;
-@property (weak) id<TravisEventFetcherDelegate> delegate;
-
-@end
-
-@protocol TravisEventFetcherDelegate <NSObject>
-
-@optional
-- (void)eventFetcher:(TravisEventFetcher *)eventFetcher gotEvent:(TravisEvent *)event;
-
-@end
++ (TravisEventFetcher *)eventFetcher;
+@end
View
20 src/TravisEventFetcher.m
@@ -17,29 +17,45 @@
#import "Reachability.h"
#import "Notification.h"
#import "NotificationDisplayer.h"
+#import <ReactiveCocoa/ReactiveCocoa.h>
@interface TravisEventFetcher () <PTPusherDelegate>
@property (strong) PTPusher *pusher;
@property (strong) PTPusherChannel *channel;
@property (strong) Reachability *reachability;
+@property (strong) RACReplaySubject *eventSubject;
@end
@implementation TravisEventFetcher
++ (TravisEventFetcher *)eventFetcher {
+ return [self new];
+}
+
- (id)init {
self = [super init];
if (self) {
_pusher = [PTPusher pusherWithKey:kPusherApiKey delegate:self encrypted:YES];
_channel = [_pusher subscribeToChannelNamed:kPusherChannelName];
[_channel bindToEventNamed:kPusherEventBuildStarted target:self action:@selector(handleEvent:)];
[_channel bindToEventNamed:kPusherEventBuildFinished target:self action:@selector(handleEvent:)];
+ _eventSubject = [RACReplaySubject subject];
}
return self;
}
+- (RACSignal *)eventStream {
+ return [[self eventSubject]
+ map:^(PTPusherEvent *event) {
+ return [[TravisEvent alloc] initWithEventData:[event data]];
+ }];
+}
+
+#pragma mark - PTPusherDelegate
+
- (void)pusher:(PTPusher *)client connectionDidConnect:(PTPusherConnection *)connection {
[client setReconnectAutomatically:YES];
}
@@ -80,9 +96,7 @@ - (void)reachabilityChanged:(NSNotification *)note {
}
- (void)handleEvent:(PTPusherEvent *)event {
- if ([[self delegate] respondsToSelector:@selector(eventFetcher:gotEvent:)]) {
- [[self delegate] eventFetcher:self gotEvent:[[TravisEvent alloc] initWithEventData:[event data]]];
- }
+ [[self eventSubject] sendNext:event];
}
@end
View
21 src/TravisHTTPClient.h
@@ -6,10 +6,25 @@
// Copyright (c) 2012 Travis CI GmbH. All rights reserved.
//
-#import <AFNetworking/AFNetworking.h>
+#import <Foundation/Foundation.h>
-@interface TravisHTTPClient : AFHTTPClient
+@class RACSignal;
-+ (TravisHTTPClient *)sharedHTTPClient;
+typedef NS_ENUM(NSInteger, TravisHTTPClientMethod) {
+ TravisHTTPClientMethodGET,
+ TravisHTTPClientMethodHEAD,
+ TravisHTTPClientMethodPOST,
+ TravisHTTPClientMethodPUT,
+ TravisHTTPClientMethodDELETE,
+ TravisHTTPClientMethodPATCH
+};
+
+@interface TravisHTTPClient : NSObject
+
++ (TravisHTTPClient *)standardHTTPClient;
+
+- (id)initWithBaseURL:(NSURL *)baseURL;
+
+- (RACSignal *)requestWithMethod:(TravisHTTPClientMethod)method path:(NSString *)path parameters:(NSDictionary *)parameters;
@end
View
65 src/TravisHTTPClient.m
@@ -7,31 +7,66 @@
//
#import "TravisHTTPClient.h"
+#import <AFNetworking/AFNetworking.h>
+#import <ReactiveCocoa/ReactiveCocoa.h>
NSString * const kTravisBaseURL = @"http://travis-ci.org";
-@implementation TravisHTTPClient
+@interface TravisHTTPClient ()
+@property (nonatomic, strong) AFHTTPClient *HTTPClient;
+@end
-+ (TravisHTTPClient *)sharedHTTPClient {
- static TravisHTTPClient *_sharedHTTPClient = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- _sharedHTTPClient = [[TravisHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:kTravisBaseURL]];
- });
+@implementation TravisHTTPClient
- return _sharedHTTPClient;
++ (TravisHTTPClient *)standardHTTPClient {
+ return [[self alloc] initWithBaseURL:[NSURL URLWithString:kTravisBaseURL]];
}
-- (id)initWithBaseURL:(NSURL *)url {
- self = [super initWithBaseURL:url];
- if (!self) {
- return nil;
- }
+- (id)initWithBaseURL:(NSURL *)baseURL {
+ self = [super init];
+ if (self == nil) return nil;
- [self registerHTTPOperationClass:[AFJSONRequestOperation class]];
- [self setDefaultHeader:@"Accept" value:@"application/json"];
+ [self setupHTTPClientWithBaseURL:baseURL];
return self;
}
+- (void)setupHTTPClientWithBaseURL:(NSURL *)baseURL {
+ [self setHTTPClient:[[AFHTTPClient alloc] initWithBaseURL:baseURL]];
+ [[self HTTPClient] registerHTTPOperationClass:[AFJSONRequestOperation class]];
+ [[self HTTPClient] setDefaultHeader:@"Accept" value:@"application/json"];
+}
+
+- (RACSignal *)requestWithMethod:(TravisHTTPClientMethod)method path:(NSString *)path parameters:(NSDictionary *)parameters {
+ RACReplaySubject *subject = [RACReplaySubject subject];
+ NSURLRequest *request = [[self HTTPClient] requestWithMethod:[self methodStringForMethod:method] path:path parameters:parameters];
+ AFHTTPRequestOperation *operation = [[self HTTPClient] HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
+ [subject sendNext:responseObject];
+ [subject sendCompleted];
+ } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
+ [subject sendError:error];
+ }];
+
+ [[self HTTPClient] enqueueHTTPRequestOperation:operation];
+
+ return subject;
+}
+
+- (NSString *)methodStringForMethod:(TravisHTTPClientMethod)method {
+ switch (method) {
+ case TravisHTTPClientMethodDELETE:
+ return @"DELETE";
+ case TravisHTTPClientMethodGET:
+ return @"GET";
+ case TravisHTTPClientMethodHEAD:
+ return @"HEAD";
+ case TravisHTTPClientMethodPATCH:
+ return @"PATCH";
+ case TravisHTTPClientMethodPOST:
+ return @"POST";
+ case TravisHTTPClientMethodPUT:
+ return @"PUT";
+ }
+}
+
@end

0 comments on commit 4bd4bf8

Please sign in to comment.