Last active
August 29, 2015 14:13
-
-
Save alexruperez/51f504da05ddb307450a to your computer and use it in GitHub Desktop.
ARURLOperation -> NSOperation subclass. NSURLRequest. NSURLConnection. NSURLResponse. Response NSData. NSError. KVO. NSThread. NSRunLoop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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