Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
testAsyncNetworking
testDispatch
testMultiTimers
23 changes: 11 additions & 12 deletions AsyncDownloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Licensed under the MIT License.
*/

#import <Foundation/Foundation.h>
@import Foundation;

// Notification name
extern NSString * const AsyncDownloaderFinishedNotification;
Expand All @@ -15,18 +15,17 @@ extern NSString * const AsyncDownloaderFinishedNotification;
extern NSString * const AsyncDownloaderFinishedNotificationSucceededKey; // @(YES) or @(NO)
extern NSString * const AsyncDownloaderFinishedNotificationErrorKey; // NSError or missing

@interface AsyncDownloader : NSObject <NSURLConnectionDelegate> {
NSURLConnection *_connection;
NSMutableData *_responseData;
}
@interface AsyncDownloader : NSObject <NSURLConnectionDelegate>

@property (getter=isCancelled) BOOL cancel;
@property (readonly) NSURL *url;
@property (readonly) NSData *responseData;
@property (readonly) NSStringEncoding responseEncoding;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly) NSError *error;
@property (getter=isCancelled) BOOL cancel;
@property (getter=isFinished,readonly) BOOL finished;

- (BOOL)downloadFromURL:(NSURL *)url;
@property (readonly) NSStringEncoding responseEncoding;

@property (readonly) NSURL * url;
@property (readonly) NSData * responseData;
@property (readonly) NSError * error;

- (BOOL) downloadFromURL:(NSURL*)url;

@end
98 changes: 45 additions & 53 deletions AsyncDownloader.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,29 @@
#define LOG(fmt, ...) /* nothing */
#endif

NSString * const AsyncDownloaderFinishedNotification = @"AsyncDownloaderFinishedNotification";
NSString * const AsyncDownloaderFinishedNotificationSucceededKey = @"AsyncDownloaderFinishedNotificationSucceeded";
NSString * const AsyncDownloaderFinishedNotificationErrorKey = @"AsyncDownloaderFinishedNotificationError";
static NSString * const _asyncDownloaderErrorDomain = @"AsyncDownloaderErrorDomain";

// Private Methods
@interface AsyncDownloader ()
NSString * const AsyncDownloaderFinishedNotification = @"AsyncDownloaderFinishedNotification",
* const AsyncDownloaderFinishedNotificationSucceededKey = @"AsyncDownloaderFinishedNotificationSucceeded",
* const AsyncDownloaderFinishedNotificationErrorKey = @"AsyncDownloaderFinishedNotificationError";

- (void)_sendFinishedNotification;
- (void)_setErrorWithString:(NSString *)message
underlyingError:(NSError *)underlyingError;

@end
static NSString * const _asyncDownloaderErrorDomain = @"AsyncDownloaderErrorDomain";

@implementation AsyncDownloader
{
NSURLConnection *_connection;
NSMutableData *_responseData;
}

@synthesize cancel = _cancel;
@synthesize url = _url;
@synthesize responseEncoding = _responseEncoding;
@synthesize finished = _finished;
@synthesize error = _error;
@synthesize url = _url, error = _error, cancel = _cancel,
finished = _finished, responseEncoding = _responseEncoding;

- (BOOL)downloadFromURL:(NSURL *)url {
_url = url;
_connection = nil;
_responseData = nil;
_cancel = NO;

_url = url;
_connection = nil;
_responseData = nil;
_cancel = NO;
_responseEncoding = NSUTF8StringEncoding;
_error = nil;
_error = nil;

NSURLRequest *request = [NSURLRequest requestWithURL:url];
if (!request) {
Expand All @@ -53,8 +47,8 @@ - (BOOL)downloadFromURL:(NSURL *)url {
return NO;
}

_connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
_connection = [NSURLConnection.alloc initWithRequest:request delegate:self];

if (!_connection) {
_finished = YES;
[self _setErrorWithString:@"Failed to create NSURLConnection object"
Expand All @@ -70,36 +64,33 @@ - (BOOL)downloadFromURL:(NSURL *)url {

#pragma mark - NSURLConnectionDelegate methods

- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response {
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response {

if (_cancel)
return;
if (_cancel) return;

NSString *textEncodingName = response.textEncodingName;
LOG(@"Received response from '%@'. Encoding=%@", _url, textEncodingName);
_responseData = [NSMutableData new];
if (textEncodingName) {
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)textEncodingName);
_responseEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
} else {
_responseEncoding = NSUTF8StringEncoding;
}
_responseData = NSMutableData.new;
_responseEncoding = textEncodingName
? CFStringConvertEncodingToNSStringEncoding(
CFStringConvertIANACharSetNameToEncoding(
(CFStringRef)textEncodingName
)
)
: NSUTF8StringEncoding;
}

- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data {
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {

if (_cancel)
return;
if (_cancel) return;

LOG(@"Received %lu bytes from '%@'", (unsigned long)[data length], _url);

[_responseData appendData:data];
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse {
- (NSCachedURLResponse *)connection:(NSURLConnection*)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
return nil; // Not necessary
}

Expand All @@ -120,28 +111,29 @@ - (void)connection:(NSURLConnection *)connection
[self _sendFinishedNotification];
}

#pragma mark - Private Methods

- (void)_sendFinishedNotification {
NSAssert(_finished, @"Download didn't finish!");

NSMutableDictionary *userInfo = [NSMutableDictionary new];
if (_error) {
userInfo[AsyncDownloaderFinishedNotificationSucceededKey] = @(NO);
userInfo[AsyncDownloaderFinishedNotificationErrorKey] = _error;
} else {
userInfo[AsyncDownloaderFinishedNotificationSucceededKey] = @(YES);
}
id userInfo =
@{AsyncDownloaderFinishedNotificationSucceededKey : @(!(_error))}.mutableCopy;

if (_error)
[userInfo setObject:_error forKey:AsyncDownloaderFinishedNotificationErrorKey];

[[NSNotificationCenter defaultCenter] postNotificationName:AsyncDownloaderFinishedNotification
object:self
[NSNotificationCenter.defaultCenter postNotificationName:AsyncDownloaderFinishedNotification
object:self
userInfo:userInfo];
}

- (void)_setErrorWithString:(NSString *)message
underlyingError:(NSError *)underlyingError {

NSMutableDictionary *userInfo = [NSMutableDictionary new];
if (message)
userInfo[NSLocalizedDescriptionKey] = message;
NSMutableDictionary *userInfo = NSMutableDictionary.new;

if (message) userInfo[NSLocalizedDescriptionKey] = message;

if (underlyingError)
userInfo[NSUnderlyingErrorKey] = underlyingError;

Expand Down
9 changes: 5 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

CC = clang
CFLAGS = -fmodules
MFLAGS = -DLOGGING -DDEBUG=1 -g -fobjc-arc
LDFLAGS = -g
LIBS = -framework Foundation
Expand All @@ -9,18 +10,18 @@ SRCS = $(wildcard *.m)
OBJS = $(SRCS:.m=.o)

%.o: %.m
$(CC) $(MFLAGS) -c -o $@ $<
$(CC) $(CFLAGS) $(MFLAGS) -c -o $@ $<

all: $(TARGETS)

testAsyncNetworking: testAsyncNetworking.o RunLoopController.o AsyncDownloader.o
$(CC) $(LDFLAGS) -o $@ testAsyncNetworking.o RunLoopController.o AsyncDownloader.o $(LIBS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ testAsyncNetworking.o RunLoopController.o AsyncDownloader.o $(LIBS)

testMultiTimers: testMultiTimers.o RunLoopController.o
$(CC) $(LDFLAGS) -o $@ testMultiTimers.o RunLoopController.o $(LIBS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ testMultiTimers.o RunLoopController.o $(LIBS)

testDispatch: testDispatch.o RunLoopController.o
$(CC) $(LDFLAGS) -o $@ testDispatch.o RunLoopController.o $(LIBS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ testDispatch.o RunLoopController.o $(LIBS)

test: $(TARGETS)
./testAsyncNetworking -s http://www.google.co.uk http://facebook.com http://stackoverflow.com http://qwerty
Expand Down
111 changes: 45 additions & 66 deletions RunLoopController.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,88 +6,67 @@
* Licensed under the MIT License.
*/

#import <Foundation/Foundation.h>
@import Foundation;

@interface RunLoopController : NSObject <NSMachPortDelegate> {
NSMachPort *_terminatePort;
BOOL _terminate;
}
@interface RunLoopController : NSObject <NSMachPortDelegate>

/**
* Get the number of registered instances.
*
* This value can be used to decide to terminate the main thread when no
* more worker threads exist.
*
* @return The number of registered instances.
/*! Set to YES to set terminate flag and signal the associated
run loop (calling the @c -signal method).
@return YES if the run loop terminate flag/should terminate, else NO.
*/
+ (NSInteger)instanceCount;
@property (nonatomic) BOOL shouldTerminate;

/**
* Retrieve the RunLoopController object associated with the current thread.
*
* @return The RunLoopController object associated with the current thread.
/*! The number of registered instances.
@note This value can be used to decide to terminate
the main thread when no more worker threads exist.
*/
+ (RunLoopController *)currentRunLoopController;
+ (NSInteger) instanceCount;

/**
* Retrieve the RunLoopController object associated with the main thread.
*
* @return The RunLoopController object associated with the main thread.
*/
+ (RunLoopController *)mainRunLoopController;
/// The RunLoopController object associated with the current thread.

/**
* Register the run loop controller with the current run loop.
*/
- (void)register;
+ (RunLoopController*) currentRunLoopController;

/**
* Deregister the run loop controller from the current run loop.
*/
- (void)deregister;
/// The RunLoopController object associated with the main thread.

/**
* Run the current run loop. This is a shortcut for:
* calling runMode:NSDefaultRunLoopModebeforeDate:[NSDate distantFuture]
*
* @return NO if the run loop was asked to terminate, or an error occurred. YES if
* the run loop finished for another reason.
*/
- (BOOL)run;
+ (RunLoopController*) mainRunLoopController;

/**
* Run the current run loop.
*
* @param mode As per [NSRunLoop runMode:limitDate:].
* @param limitDate As per [NSRunLoop runMode:limitDate:].
*
* @return NO if the run loop was asked to terminate, or an error occurred. YES if
* the run loop finished for another reason.
*/
- (BOOL)runMode:(NSString *)mode
beforeDate:(NSDate *)limitDate;
/// Register the run loop controller with the current run loop.

- (void) register;

/// Deregister the run loop controller from the current run loop.

/**
* Set the terminate flag and signal the associated run loop (calling the
* -signal method).
- (void) deregister;

/*! Run the current run loop.
@note This is a shortcut for:
@see runMode:NSDefaultRunLoopModebeforeDate: with NSDate.distantFuture
@return NO if the run loop was asked to terminate, or an error occurred.
YES if the run loop finished for another reason.
*/
- (void)terminate;
- (BOOL) run;

/**
* Determine if the terminate flag is set.
*
* @return YES if the run loop should terminate, else NO.
/*! Run the current run loop.
@param mode As per [NSRunLoop runMode:limitDate:].
@param limitDate As per [NSRunLoop runMode:limitDate:].
@return NO the run loop was asked to terminate, or an error occurred.
YES the run loop finished for another reason.
*/
- (BOOL)shouldTerminate;
- (BOOL) runMode:(NSString*)mode beforeDate:(NSDate*)limitDate;

/**
* Signal the run loop associated with the run loop controller.
*
* This is useful if you want to wake up a run loop so the code running the loop
* can evaluate conditions for termination (normally the main thread run loop).
/*! Signal the run loop associated with the run loop controller.
@note This is useful if you want to wake up a run loop so the code running the loop
can evaluate conditions for termination (normally the main thread run loop).
*/
- (void)signal;
- (void) signal;

@end


#ifdef WANT_RUNLOOP_LOGGER
#ifdef LOGGING
#define LOG(fmt, ...) NSLog(fmt, ## __VA_ARGS__)
#else
#define LOG(fmt, ...) /* nothing */
#endif
#endif
Loading