Skip to content

Commit 0489fc1

Browse files
committed
Initial Commit
0 parents  commit 0489fc1

22 files changed

Lines changed: 1178 additions & 0 deletions

.gitignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Xcode
2+
.DS_Store
3+
build/
4+
*.pbxuser
5+
!default.pbxuser
6+
*.mode1v3
7+
!default.mode1v3
8+
*.mode2v3
9+
!default.mode2v3
10+
*.perspectivev3
11+
!default.perspectivev3
12+
*.xcworkspace
13+
!default.xcworkspace
14+
xcuserdata
15+
profile
16+
*.moved-aside
17+
DerivedData

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
language: objective-c
2+
xcode_sdk:
3+
- iphonesimulator6.0

EventSource.podspec

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Pod::Spec.new do |s|
2+
s.name = "EventSource"
3+
s.version = "1.0.0"
4+
s.summary = "HTML5 Server-Sent Events in your Cocoa app."
5+
s.homepage = "http://github.com/neilco/EventSource"
6+
s.license = 'MIT (see LICENSE.txt)'
7+
s.author = { "Neil Cowburn" => "git@neilcowburn.com" }
8+
s.source = { :git => "https://github.com/neilco/EventSource.git", :tag => "1.0.0" }
9+
s.source_files = 'EventSource', 'EventSource/EventSource.{h,m}'
10+
s.ios.xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => '$(inherited) "$(SDKROOT)/Developer/Library/Frameworks" "$(DEVELOPER_LIBRARY_DIR)/Frameworks"' }
11+
s.osx.xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => '$(inherited) "$(DEVELOPER_LIBRARY_DIR)/Frameworks"' }
12+
s.ios.deployment_target = '5.0'
13+
s.osx.deployment_target = '10.7'
14+
s.requires_arc = true
15+
s.xcconfig = { 'OTHER_LDFLAGS' => '-lObjC' }
16+
end

EventSource.xcodeproj/project.pbxproj

Lines changed: 413 additions & 0 deletions
Large diffs are not rendered by default.

EventSource/EventSource-Prefix.pch

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//
2+
// Prefix header for all source files of the 'EventSource' target in the 'EventSource' project
3+
//
4+
5+
#ifdef __OBJC__
6+
#import <Foundation/Foundation.h>
7+
#endif

EventSource/EventSource.h

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//
2+
// EventSource.h
3+
// EventSource
4+
//
5+
// Created by Neil on 25/07/2013.
6+
// Copyright (c) 2013 Neil Cowburn. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
typedef enum {
12+
kEventStateConnecting = 0,
13+
kEventStateOpen = 1,
14+
kEventStateClosed = 2,
15+
} EventState;
16+
17+
// ---------------------------------------------------------------------------------------------------------------------
18+
19+
/// Describes an Event received from an EventSource
20+
@interface Event : NSObject
21+
22+
/// The Event ID
23+
@property (nonatomic, strong) id id;
24+
/// The name of the Event
25+
@property (nonatomic, strong) NSString *event;
26+
/// The data received from the EventSource
27+
@property (nonatomic, strong) NSString *data;
28+
29+
/// The current state of the connection to the EventSource
30+
@property (nonatomic, assign) EventState readyState;
31+
/// Provides details of any errors with the connection to the EventSource
32+
@property (nonatomic, strong) NSError *error;
33+
34+
@end
35+
36+
// ---------------------------------------------------------------------------------------------------------------------
37+
38+
typedef void (^EventSourceEventHandler)(Event *event);
39+
40+
// ---------------------------------------------------------------------------------------------------------------------
41+
42+
/// Connect to and receive Server-Sent Events (SSEs).
43+
@interface EventSource : NSObject
44+
45+
/// Returns a new instance of EventSource with the specified URL.
46+
///
47+
/// @param URL The URL of the EventSource.
48+
+ (id)eventSourceWithURL:(NSURL *)URL;
49+
50+
/// Creates a new instance of EventSource with the specified URL.
51+
///
52+
/// @param URL The URL of the EventSource.
53+
- (id)initWithURL:(NSURL *)URL;
54+
55+
/// Registers an event handler for the Message event.
56+
///
57+
/// @param handler The handler for the Message event.
58+
- (void)onMessage:(EventSourceEventHandler)handler;
59+
60+
/// Registers an event handler for the Error event.
61+
///
62+
/// @param handler The handler for the Error event.
63+
- (void)onError:(EventSourceEventHandler)handler;
64+
65+
/// Registers an event handler for the Open event.
66+
///
67+
/// @param handler The handler for the Open event.
68+
- (void)onOpen:(EventSourceEventHandler)handler;
69+
70+
/// Registers an event handler for a named event.
71+
///
72+
/// @param eventName The name of the event you registered.
73+
/// @param handler The handler for the Message event.
74+
- (void)addEventListener:(NSString *)eventName handler:(EventSourceEventHandler)handler;
75+
76+
/// Closes the connection to the EventSource.
77+
- (void)close;
78+
79+
@end
80+
81+
// ---------------------------------------------------------------------------------------------------------------------
82+
83+
extern NSString *const MessageEvent;
84+
extern NSString *const ErrorEvent;
85+
extern NSString *const OpenEvent;

EventSource/EventSource.m

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
//
2+
// EventSource.m
3+
// EventSource
4+
//
5+
// Created by Neil on 25/07/2013.
6+
// Copyright (c) 2013 Neil Cowburn. All rights reserved.
7+
//
8+
9+
#import "EventSource.h"
10+
11+
#define ES_RECONNECT_TIMEOUT 1.0
12+
13+
@interface EventSource () <NSURLConnectionDelegate, NSURLConnectionDataDelegate> {
14+
NSURL *eventURL;
15+
NSURLConnection *eventSource;
16+
NSMutableDictionary *listeners;
17+
BOOL wasClosed;
18+
}
19+
20+
- (void)open;
21+
22+
@end
23+
24+
@implementation EventSource
25+
26+
+ (id)eventSourceWithURL:(NSURL *)URL
27+
{
28+
return [[EventSource alloc] initWithURL:URL];
29+
}
30+
31+
- (id)initWithURL:(NSURL *)URL
32+
{
33+
if (self = [super init]) {
34+
listeners = [NSMutableDictionary dictionary];
35+
eventURL = URL;
36+
37+
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(ES_RECONNECT_TIMEOUT * NSEC_PER_SEC));
38+
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
39+
[self open];
40+
});
41+
}
42+
return self;
43+
}
44+
45+
- (void)addEventListener:(NSString *)eventName handler:(EventSourceEventHandler)handler
46+
{
47+
if (listeners[eventName] == nil) {
48+
[listeners setObject:[NSMutableArray array] forKey:eventName];
49+
}
50+
51+
[listeners[eventName] addObject:handler];
52+
}
53+
54+
- (void)onMessage:(EventSourceEventHandler)handler
55+
{
56+
[self addEventListener:MessageEvent handler:handler];
57+
}
58+
59+
- (void)onError:(EventSourceEventHandler)handler
60+
{
61+
[self addEventListener:ErrorEvent handler:handler];
62+
}
63+
64+
- (void)onOpen:(EventSourceEventHandler)handler
65+
{
66+
[self addEventListener:OpenEvent handler:handler];
67+
}
68+
69+
- (void)open
70+
{
71+
wasClosed = NO;
72+
NSURLRequest *request = [NSURLRequest requestWithURL:eventURL];
73+
eventSource = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
74+
}
75+
76+
- (void)close
77+
{
78+
wasClosed = YES;
79+
[eventSource cancel];
80+
}
81+
82+
// ---------------------------------------------------------------------------------------------------------------------
83+
84+
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
85+
{
86+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
87+
if (httpResponse.statusCode == 200) {
88+
// Opened
89+
Event *e = [Event new];
90+
e.readyState = kEventStateOpen;
91+
92+
NSArray *openHandlers = listeners[OpenEvent];
93+
for (EventSourceEventHandler handler in openHandlers) {
94+
dispatch_async(dispatch_get_main_queue(), ^{
95+
handler(e);
96+
});
97+
}
98+
}
99+
}
100+
101+
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
102+
{
103+
Event *e = [Event new];
104+
e.readyState = kEventStateClosed;
105+
e.error = error;
106+
107+
NSArray *errorHandlers = listeners[ErrorEvent];
108+
for (EventSourceEventHandler handler in errorHandlers) {
109+
dispatch_async(dispatch_get_main_queue(), ^{
110+
handler(e);
111+
});
112+
}
113+
114+
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(ES_RECONNECT_TIMEOUT * NSEC_PER_SEC));
115+
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
116+
[self open];
117+
});
118+
}
119+
120+
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
121+
{
122+
__block NSString *eventString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
123+
124+
if ([eventString hasSuffix:@"\n\n"]) {
125+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
126+
eventString = [eventString stringByReplacingOccurrencesOfString:@"\n\n" withString:@""];
127+
NSMutableArray *components = [[eventString componentsSeparatedByString:@"\n"] mutableCopy];
128+
129+
Event *e = [Event new];
130+
e.readyState = kEventStateOpen;
131+
132+
for (NSString *component in components) {
133+
NSArray *pairs = [component componentsSeparatedByString:@": "];
134+
if ([component hasPrefix:@"id"]) {
135+
e.id = pairs[1];
136+
} else if ([component hasPrefix:@"event"]) {
137+
e.event = pairs[1];
138+
} else if ([component hasPrefix:@"data"]) {
139+
e.data = pairs[1];
140+
}
141+
}
142+
143+
NSArray *messageHandlers = listeners[MessageEvent];
144+
for (EventSourceEventHandler handler in messageHandlers) {
145+
dispatch_async(dispatch_get_main_queue(), ^{
146+
handler(e);
147+
});
148+
}
149+
150+
if (e.event != nil) {
151+
NSArray *namedEventhandlers = listeners[e.event];
152+
for (EventSourceEventHandler handler in namedEventhandlers) {
153+
dispatch_async(dispatch_get_main_queue(), ^{
154+
handler(e);
155+
});
156+
}
157+
}
158+
});
159+
}
160+
}
161+
162+
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
163+
{
164+
if (wasClosed) {
165+
return;
166+
}
167+
168+
Event *e = [Event new];
169+
e.readyState = kEventStateClosed;
170+
e.error = [NSError errorWithDomain:@""
171+
code:e.readyState
172+
userInfo:@{ NSLocalizedDescriptionKey: @"Connection with the event source was closed." }];
173+
174+
NSArray *errorHandlers = listeners[ErrorEvent];
175+
for (EventSourceEventHandler handler in errorHandlers) {
176+
dispatch_async(dispatch_get_main_queue(), ^{
177+
handler(e);
178+
});
179+
}
180+
181+
[self open];
182+
}
183+
184+
@end
185+
186+
// ---------------------------------------------------------------------------------------------------------------------
187+
188+
@implementation Event
189+
190+
- (NSString *)description
191+
{
192+
NSString *state = nil;
193+
switch (self.readyState) {
194+
case kEventStateConnecting:
195+
state = @"CONNECTING";
196+
break;
197+
case kEventStateOpen:
198+
state = @"OPEN";
199+
break;
200+
case kEventStateClosed:
201+
state = @"CLOSED";
202+
break;
203+
}
204+
205+
return [NSString stringWithFormat:@"<%@: readyState: %@, id: %@; event: %@; data: %@>",
206+
[self class],
207+
state,
208+
self.id,
209+
self.event,
210+
self.data];
211+
}
212+
213+
@end
214+
215+
NSString *const MessageEvent = @"message";
216+
NSString *const ErrorEvent = @"error";
217+
NSString *const OpenEvent = @"open";

EventViewer/AppDelegate.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// AppDelegate.h
3+
// EventViewer
4+
//
5+
// Created by Neil on 25/07/2013.
6+
// Copyright (c) 2013 Neil Cowburn. All rights reserved.
7+
//
8+
9+
#import <UIKit/UIKit.h>
10+
11+
@class ViewController;
12+
13+
@interface AppDelegate : UIResponder <UIApplicationDelegate>
14+
15+
@property (strong, nonatomic) UIWindow *window;
16+
17+
@property (strong, nonatomic) ViewController *viewController;
18+
19+
@end

0 commit comments

Comments
 (0)