Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
ddunkin committed Mar 14, 2011
1 parent 1ad95be commit 6bd8c1c
Show file tree
Hide file tree
Showing 14 changed files with 856 additions and 10 deletions.
146 changes: 139 additions & 7 deletions CometClient.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion CometClient/MainViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
//

#import "FlipsideViewController.h"
#import "DDCometClient.h"

@interface MainViewController : UIViewController <FlipsideViewControllerDelegate> {
@interface MainViewController : UIViewController <FlipsideViewControllerDelegate, DDCometClientDelegate> {

}

Expand Down
39 changes: 37 additions & 2 deletions CometClient/MainViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,51 @@
//

#import "MainViewController.h"
#import "DDCometClient.h"
#import "DDCometMessage.h"


@implementation MainViewController

/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
DDCometClient *client = [[DDCometClient alloc] initWithURL:[NSURL URLWithString:@"http://localhost:8080/cometd"]];
client.delegate = self;
[client handshake:NULL];
}

- (void)cometClientHandshakeDidSucceed:(DDCometClient *)client
{
NSLog(@"Handshake succeeded");
[client subscribeToChannel:@"/chat/demo" target:self selector:@selector(chatMessageReceived:) error:NULL];
[client subscribeToChannel:@"/members/demo" target:self selector:@selector(membershipMessageReceived:) error:NULL];
}

- (void)cometClient:(DDCometClient *)client handshakeDidFailWithError:(NSError *)error
{
NSLog(@"Handshake failed");
}

- (void)cometClient:(DDCometClient *)client subscriptionDidSucceed:(DDCometSubscription *)subscription
{
NSLog(@"Subsription succeeded");
}

- (void)cometClient:(DDCometClient *)client subscription:(DDCometSubscription *)subscription didFailWithError:(NSError *)error
{
NSLog(@"Subsription failed");
}

- (void)chatMessageReceived:(DDCometMessage *)message
{
NSLog(@"%@: %@", [message.data objectForKey:@"user"], [message.data objectForKey:@"chat"]);
}

- (void)membershipMessageReceived:(DDCometMessage *)message
{
}
*/

- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller
{
Expand Down
57 changes: 57 additions & 0 deletions DDComet/DDCometClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

#import <Foundation/Foundation.h>


@class DDCometClientOperation;
@class DDCometMessage;
@class DDCometSubscription;
@protocol DDCometClientDelegate;
@protocol DDQueue;

typedef enum {
DDCometStateDisconnected,
DDCometStateConnecting,
DDCometStateConnected
} DDCometState;

@interface DDCometClient : NSObject {
@private
NSURL *m_endpointURL;
volatile int32_t m_messageCounter;
NSMutableDictionary *m_pendingSubscriptions; // by id
NSMutableArray *m_subscriptions;
DDCometState m_state;
id<DDQueue> m_outgoingQueue;
id<DDQueue> m_incomingQueue;
NSOperationQueue *m_communicationOperationQueue;
}

@property (nonatomic, readonly) NSString *clientID;
@property (nonatomic, readonly) NSURL *endpointURL;
@property (nonatomic, assign) id<DDCometClientDelegate> delegate;

- (id)initWithURL:(NSURL *)endpointURL;
- (BOOL)handshake:(NSError **)error;
- (BOOL)connect:(NSError **)error;
- (void)disconnect;
- (BOOL)subscribeToChannel:(NSString *)channel target:(id)target selector:(SEL)selector error:(NSError **)error;
- (BOOL)unsubsubscribeFromChannel:(NSString *)channel target:(id)target selector:(SEL)selector error:(NSError **)error;
- (BOOL)publishMessage:(DDCometMessage *)message toChannel:(NSString *)channel error:(NSError **)error;

@end

@interface DDCometClient (Internal)

- (id<DDQueue>)outgoingQueue;
- (id<DDQueue>)incomingQueue;
- (void)operationDidFinish:(DDCometClientOperation *)operation;

@end

@protocol DDCometClientDelegate <NSObject>
@optional
- (void)cometClientHandshakeDidSucceed:(DDCometClient *)client;
- (void)cometClient:(DDCometClient *)client handshakeDidFailWithError:(NSError *)error;
- (void)cometClient:(DDCometClient *)client subscriptionDidSucceed:(DDCometSubscription *)subscription;
- (void)cometClient:(DDCometClient *)client subscription:(DDCometSubscription *)subscription didFailWithError:(NSError *)error;
@end
208 changes: 208 additions & 0 deletions DDComet/DDCometClient.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@

#import "DDCometClient.h"
#import <libkern/OSAtomic.h>
#import "DDCometClientOperation.h"
#import "DDCometMessage.h"
#import "DDCometSubscription.h"
#import "NSMutableArray+Queue.h"


@interface DDCometClient ()

- (NSString *)nextMessageID;
- (void)sendMessage:(DDCometMessage *)message;
- (void)handleMessage:(DDCometMessage *)message;

@end

@implementation DDCometClient

@synthesize clientID = m_clientID,
endpointURL = m_endpointURL,
delegate = m_delegate;

- (id)initWithURL:(NSURL *)endpointURL
{
if ((self = [super init]))
{
m_endpointURL = [endpointURL retain];
m_pendingSubscriptions = [[NSMutableDictionary alloc] init];
m_subscriptions = [[NSMutableArray alloc] init];
m_outgoingQueue = [[NSMutableArray alloc] init];
m_incomingQueue = [[NSMutableArray alloc] init];
m_communicationOperationQueue = [[NSOperationQueue alloc] init];
[m_communicationOperationQueue setMaxConcurrentOperationCount:1];
[self performSelectorInBackground:@selector(processIncomingMessages) withObject:nil];
}
return self;
}

- (void)dealloc
{
[m_communicationOperationQueue release];
[m_incomingQueue release];
[m_outgoingQueue release];
[m_subscriptions release];
[m_pendingSubscriptions release];
[m_endpointURL release];
[m_clientID release];
[super dealloc];
}

- (BOOL)handshake:(NSError **)error
{
m_state = DDCometStateConnecting;

DDCometMessage *message = [DDCometMessage messageWithChannel:@"/meta/handshake"];
message.version = @"1.0";
message.supportedConnectionTypes = [NSArray arrayWithObject:@"long-polling"];

[self sendMessage:message];
return YES;
}

- (BOOL)connect:(NSError **)error
{
return YES;
}

- (void)disconnect
{

}

- (BOOL)subscribeToChannel:(NSString *)channel target:(id)target selector:(SEL)selector error:(NSError **)error
{
DDCometMessage *message = [DDCometMessage messageWithChannel:@"/meta/subscribe"];
message.ID = [self nextMessageID];
message.subscription = channel;
DDCometSubscription *subscription = [[[DDCometSubscription alloc] initWithChannel:channel target:target selector:selector] autorelease];
@synchronized(m_pendingSubscriptions)
{
[m_pendingSubscriptions setObject:subscription forKey:message.ID];
}
[self sendMessage:message];
return YES;
}

- (BOOL)unsubsubscribeFromChannel:(NSString *)channel target:(id)target selector:(SEL)selector error:(NSError **)error
{
return YES;
}

- (BOOL)publishMessage:(DDCometMessage *)message toChannel:(NSString *)channel error:(NSError **)error
{
return YES;
}

#pragma mark -

- (id<DDQueue>)outgoingQueue
{
return m_outgoingQueue;
}

- (id<DDQueue>)incomingQueue
{
return m_incomingQueue;
}

- (void)operationDidFinish:(DDCometClientOperation *)operation
{
if (m_state != DDCometStateDisconnected)
[m_communicationOperationQueue addOperation:[[[DDCometClientOperation alloc] initWithClient:self] autorelease]];
}

#pragma mark -

- (NSString *)nextMessageID
{
return [NSString stringWithFormat:@"%d", OSAtomicIncrement32Barrier(&m_messageCounter)];
}

- (void)sendMessage:(DDCometMessage *)message
{
message.clientID = m_clientID;
if (!message.ID)
message.ID = [self nextMessageID];
[m_outgoingQueue addObject:message];

if ([m_communicationOperationQueue operationCount] == 0)
[m_communicationOperationQueue addOperation:[[[DDCometClientOperation alloc] initWithClient:self] autorelease]];
}

- (void)handleMessage:(DDCometMessage *)message
{
NSLog(@"%@", message);
NSString *channel = message.channel;
if ([channel isEqualToString:@"/meta/handshake"])
{
if ([message.successful boolValue])
{
m_clientID = [message.clientID retain];
m_state = DDCometStateConnected;
if (m_delegate && [m_delegate respondsToSelector:@selector(cometClientHandshakeDidSucceed:)])
[m_delegate cometClientHandshakeDidSucceed:self];
}
else
{
m_state = DDCometStateDisconnected;
if (m_delegate && [m_delegate respondsToSelector:@selector(cometClient:handshakeDidFailWithError:)])
[m_delegate cometClient:self handshakeDidFailWithError:message.error];
}
}
else if ([channel isEqualToString:@"/meta/subscribe"])
{
DDCometSubscription *subscription = nil;
@synchronized(m_pendingSubscriptions)
{
subscription = [[[m_pendingSubscriptions objectForKey:message.ID] retain] autorelease];
if (subscription)
[m_pendingSubscriptions removeObjectForKey:message.ID];
}
if ([message.successful boolValue])
{
@synchronized(m_subscriptions)
{
[m_subscriptions addObject:subscription];
}
if (m_delegate && [m_delegate respondsToSelector:@selector(cometClient:subscriptionDidSucceed:)])
[m_delegate cometClient:self subscriptionDidSucceed:subscription];
}
else
{
if (m_delegate && [m_delegate respondsToSelector:@selector(cometClient:subscription:didFailWithError:)])
[m_delegate cometClient:self subscription:subscription didFailWithError:message.error];
}
}
else
{
NSMutableArray *subscriptions = [NSMutableArray array];
@synchronized(m_subscriptions)
{
for (DDCometSubscription *subscription in m_subscriptions)
{
if ([channel isEqualToString:subscription.channel]) // TODO: handle wildcards
[subscriptions addObject:subscription];
}
}
for (DDCometSubscription *subscription in subscriptions)
[subscription.target performSelector:subscription.selector withObject:message];
}
}

- (void)processIncomingMessages
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

while (YES)
{
DDCometMessage *message;
while ((message = [m_incomingQueue removeObject]))
[self handleMessage:message];
[NSThread sleepForTimeInterval:0.1];
}

[pool release];
}
@end
15 changes: 15 additions & 0 deletions DDComet/DDCometClientOperation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

#import <Foundation/Foundation.h>


@class DDCometClient;

@interface DDCometClientOperation : NSOperation {
@private
DDCometClient *m_client;
NSMutableData *m_responseData;
}

- (id)initWithClient:(DDCometClient *)client;

@end
Loading

0 comments on commit 6bd8c1c

Please sign in to comment.