Skip to content

Instantly share code, notes, and snippets.

@alexruperez
Last active August 29, 2015 14:13
Show Gist options
  • Save alexruperez/51f504da05ddb307450a to your computer and use it in GitHub Desktop.
Save alexruperez/51f504da05ddb307450a to your computer and use it in GitHub Desktop.

Revisions

  1. alexruperez revised this gist Jan 14, 2015. 2 changed files with 73 additions and 14 deletions.
    2 changes: 1 addition & 1 deletion ARURLOperation.h
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,7 @@

    #import <Foundation/Foundation.h>

    @interface ARURLOperation : NSOperation
    @interface ARURLOperation : NSOperation <NSURLConnectionDataDelegate, NSSecureCoding, NSCopying>

    @property (nonatomic, strong, readonly) NSURLRequest *request;

    85 changes: 72 additions & 13 deletions ARURLOperation.m
    Original file line number Diff line number Diff line change
    @@ -8,15 +8,17 @@

    #import "ARURLOperation.h"

    @interface ARURLOperation () <NSURLConnectionDataDelegate>
    @interface ARURLOperation ()
    {
    BOOL _executing;
    BOOL _finished;
    }

    @property (nonatomic, strong) NSURLConnection *connection;
    @property (nonatomic, strong, readwrite) NSURLConnection *connection;

    @property (nonatomic, strong) NSMutableData *responseMutableData;
    @property (nonatomic, strong, readwrite) NSMutableData *responseMutableData;

    @property (nonatomic, strong, readwrite) NSRecursiveLock *lock;

    @end

    @@ -26,10 +28,10 @@ - (void)networkRequestThreadEntryPoint:(id)__unused object
    {
    @autoreleasepool
    {
    [[NSThread currentThread] setName:[NSString stringWithFormat:@"%@Thread", NSStringFromClass(self.class)]];
    [NSThread currentThread].name = [NSString stringWithFormat:@"%@Thread", NSStringFromClass(self.class)];

    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    NSRunLoop *runLoop = NSRunLoop.currentRunLoop;
    [runLoop addPort:NSMachPort.port forMode:NSDefaultRunLoopMode];
    [runLoop run];
    }
    }
    @@ -58,6 +60,8 @@ - (instancetype)init

    - (instancetype)initWithRequest:(NSURLRequest *)request
    {
    NSParameterAssert(request);

    self = [super init];

    if (self)
    @@ -66,6 +70,8 @@ - (instancetype)initWithRequest:(NSURLRequest *)request
    _finished = NO;
    _request = request;

    self.lock = NSRecursiveLock.new;
    self.lock.name = [NSString stringWithFormat:@"%@Lock", NSStringFromClass(self.class)];
    self.name = NSStringFromClass(self.class);
    }

    @@ -74,10 +80,10 @@ - (instancetype)initWithRequest:(NSURLRequest *)request

    - (NSData *)responseData
    {
    return [self.responseMutableData copy];
    return [NSData dataWithBytes:self.responseMutableData.bytes length:self.responseMutableData.length];
    }

    - (BOOL)isConcurrent
    - (BOOL)isAsynchronous
    {
    return YES;
    }
    @@ -92,8 +98,18 @@ - (BOOL)isFinished
    return _finished;
    }

    - (NSString *)description
    {
    [self.lock lock];
    NSString *description = [NSString stringWithFormat:@"<%@: %p, cancelled: %@ request: %@, response: %@>", NSStringFromClass(self.class), self, (self.isCancelled ? @"YES" : @"NO"), self.request, self.response];
    [self.lock unlock];
    return description;
    }

    - (void)start
    {
    [self.lock lock];

    if (self.isCancelled)
    {
    [self willChangeValueForKey:@"isFinished"];
    @@ -108,23 +124,32 @@ - (void)start
    {
    [self willChangeValueForKey:@"isExecuting"];

    [self performSelector:@selector(main) onThread:[self networkRequestThread] withObject:nil waitUntilDone:NO modes:@[NSRunLoopCommonModes]];
    [self performSelector:@selector(main) onThread:self.networkRequestThread withObject:nil waitUntilDone:NO modes:@[NSRunLoopCommonModes]];

    _executing = YES;

    [self didChangeValueForKey:@"isExecuting"];
    }

    [self.lock unlock];
    }

    - (void)main
    {
    self.responseMutableData = [[NSMutableData alloc] init];
    [self.lock lock];

    if (!self.isCancelled && [NSURLConnection canHandleRequest:self.request])
    {
    self.responseMutableData = NSMutableData.new;

    self.connection = [[NSURLConnection alloc] initWithRequest:_request delegate:self startImmediately:NO];
    self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];

    [self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    [self.connection scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];

    [self.connection start];
    }

    [self.connection start];
    [self.lock unlock];
    }

    - (void)completeOperation
    @@ -139,6 +164,8 @@ - (void)completeOperation
    [self didChangeValueForKey:@"isFinished"];
    }

    # pragma mark - NSURLConnectionDataDelegate

    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
    if (!self.isCancelled)
    @@ -163,14 +190,46 @@ - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLRespon

    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
    self.connection = nil;

    [self completeOperation];
    }

    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
    _error = error;

    self.connection = nil;

    [self completeOperation];
    }

    #pragma mark - NSSecureCoding

    + (BOOL)supportsSecureCoding
    {
    return YES;
    }

    - (id)initWithCoder:(NSCoder *)decoder
    {
    NSURLRequest *request = [decoder decodeObjectOfClass:NSURLRequest.class forKey:NSStringFromSelector(@selector(request))];

    self = [self initWithRequest:request];

    return self;
    }

    - (void)encodeWithCoder:(NSCoder *)coder
    {
    [coder encodeObject:self.request forKey:NSStringFromSelector(@selector(request))];
    }

    #pragma mark - NSCopying

    - (id)copyWithZone:(NSZone *)zone
    {
    return [(ARURLOperation *)[self.class allocWithZone:zone] initWithRequest:self.request];;
    }

    @end
  2. alexruperez created this gist Jan 13, 2015.
    25 changes: 25 additions & 0 deletions ARURLOperation.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    //
    // ARURLOperation.h
    // ARURLOperation
    //
    // Created by alexruperez on 13/1/15.
    //
    //

    #import <Foundation/Foundation.h>

    @interface ARURLOperation : NSOperation

    @property (nonatomic, strong, readonly) NSURLRequest *request;

    @property (nonatomic, strong, readonly) NSURLResponse *response;

    @property (nonatomic, strong, readonly) NSData *responseData;

    @property (nonatomic, strong, readonly) NSError *error;

    + (instancetype)operationWithRequest:(NSURLRequest *)request;

    - (instancetype)initWithRequest:(NSURLRequest *)request NS_DESIGNATED_INITIALIZER;

    @end
    176 changes: 176 additions & 0 deletions ARURLOperation.m
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,176 @@
    //
    // ARURLOperation.m
    // ARURLOperation
    //
    // Created by alexruperez on 13/1/15.
    //
    //

    #import "ARURLOperation.h"

    @interface ARURLOperation () <NSURLConnectionDataDelegate>
    {
    BOOL _executing;
    BOOL _finished;
    }

    @property (nonatomic, strong) NSURLConnection *connection;

    @property (nonatomic, strong) NSMutableData *responseMutableData;

    @end

    @implementation ARURLOperation

    - (void)networkRequestThreadEntryPoint:(id)__unused object
    {
    @autoreleasepool
    {
    [[NSThread currentThread] setName:[NSString stringWithFormat:@"%@Thread", NSStringFromClass(self.class)]];

    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    [runLoop run];
    }
    }

    - (NSThread *)networkRequestThread
    {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
    _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
    [_networkRequestThread start];
    });

    return _networkRequestThread;
    }

    + (instancetype)operationWithRequest:(NSURLRequest *)request
    {
    return [[self alloc] initWithRequest:request];
    }

    - (instancetype)init
    {
    return [self initWithRequest:nil];
    }

    - (instancetype)initWithRequest:(NSURLRequest *)request
    {
    self = [super init];

    if (self)
    {
    _executing = NO;
    _finished = NO;
    _request = request;

    self.name = NSStringFromClass(self.class);
    }

    return self;
    }

    - (NSData *)responseData
    {
    return [self.responseMutableData copy];
    }

    - (BOOL)isConcurrent
    {
    return YES;
    }

    - (BOOL)isExecuting
    {
    return _executing;
    }

    - (BOOL)isFinished
    {
    return _finished;
    }

    - (void)start
    {
    if (self.isCancelled)
    {
    [self willChangeValueForKey:@"isFinished"];

    _finished = YES;

    [self didChangeValueForKey:@"isFinished"];

    return;
    }
    else if (self.isReady)
    {
    [self willChangeValueForKey:@"isExecuting"];

    [self performSelector:@selector(main) onThread:[self networkRequestThread] withObject:nil waitUntilDone:NO modes:@[NSRunLoopCommonModes]];

    _executing = YES;

    [self didChangeValueForKey:@"isExecuting"];
    }
    }

    - (void)main
    {
    self.responseMutableData = [[NSMutableData alloc] init];

    self.connection = [[NSURLConnection alloc] initWithRequest:_request delegate:self startImmediately:NO];

    [self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

    [self.connection start];
    }

    - (void)completeOperation
    {
    [self willChangeValueForKey:@"isFinished"];
    [self willChangeValueForKey:@"isExecuting"];

    _executing = NO;
    _finished = YES;

    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
    }

    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
    if (!self.isCancelled)
    {
    [self.responseMutableData appendData:data];
    return;
    }

    [self completeOperation];
    }

    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
    if (!self.isCancelled)
    {
    _response = response;
    return;
    }

    [self completeOperation];
    }

    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
    [self completeOperation];
    }

    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
    _error = error;

    [self completeOperation];
    }

    @end