Skip to content

Instantly share code, notes, and snippets.

@alexruperez
Last active August 29, 2015 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexruperez/51f504da05ddb307450a to your computer and use it in GitHub Desktop.
Save alexruperez/51f504da05ddb307450a to your computer and use it in GitHub Desktop.
ARURLOperation -> NSOperation subclass. NSURLRequest. NSURLConnection. NSURLResponse. Response NSData. NSError. KVO. NSThread. NSRunLoop.
//
// ARURLOperation.h
// ARURLOperation
//
// Created by alexruperez on 13/1/15.
//
//
#import <Foundation/Foundation.h>
@interface ARURLOperation : NSOperation <NSURLConnectionDataDelegate, NSSecureCoding, NSCopying>
@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
//
// ARURLOperation.m
// ARURLOperation
//
// Created by alexruperez on 13/1/15.
//
//
#import "ARURLOperation.h"
@interface ARURLOperation ()
{
BOOL _executing;
BOOL _finished;
}
@property (nonatomic, strong, readwrite) NSURLConnection *connection;
@property (nonatomic, strong, readwrite) NSMutableData *responseMutableData;
@property (nonatomic, strong, readwrite) NSRecursiveLock *lock;
@end
@implementation ARURLOperation
- (void)networkRequestThreadEntryPoint:(id)__unused object
{
@autoreleasepool
{
[NSThread currentThread].name = [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
{
NSParameterAssert(request);
self = [super init];
if (self)
{
_executing = NO;
_finished = NO;
_request = request;
self.lock = NSRecursiveLock.new;
self.lock.name = [NSString stringWithFormat:@"%@Lock", NSStringFromClass(self.class)];
self.name = NSStringFromClass(self.class);
}
return self;
}
- (NSData *)responseData
{
return [NSData dataWithBytes:self.responseMutableData.bytes length:self.responseMutableData.length];
}
- (BOOL)isAsynchronous
{
return YES;
}
- (BOOL)isExecuting
{
return _executing;
}
- (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"];
_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"];
}
[self.lock unlock];
}
- (void)main
{
[self.lock lock];
if (!self.isCancelled && [NSURLConnection canHandleRequest:self.request])
{
self.responseMutableData = NSMutableData.new;
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
[self.connection scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
[self.connection start];
}
[self.lock unlock];
}
- (void)completeOperation
{
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
_executing = NO;
_finished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
# pragma mark - NSURLConnectionDataDelegate
- (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.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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment