Last active
October 14, 2015 14:44
-
-
Save diogot/3f3ba09cefc36f229498 to your computer and use it in GitHub Desktop.
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
// | |
// DTNetworkOperation.h | |
// DTOperation | |
// | |
// Created by Diogo on 10/14/15. | |
// Copyright © 2015 Diogo Tridapalli. All rights reserved. | |
// | |
#import "DTOperation.h" | |
/** | |
* Class to encapsulate a NSURLSessionTask inside a NSOperation | |
*/ | |
@interface DTNetworkOperation : DTOperation | |
/** | |
* Default method to create an operation, the operation should be put on a queue ou | |
* started manually, it's a good idea to keep a strong reference to the instance | |
* while the operation is not finished | |
* | |
* @param task NSURLSessionTask to be executed | |
* | |
* @return NSOperation | |
*/ | |
+ (instancetype)operationWithTask:(NSURLSessionTask *)task; | |
@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
// | |
// DTNetworkOperation.m | |
// DTOperation | |
// | |
// Created by Diogo on 10/14/15. | |
// Copyright © 2015 Diogo Tridapalli. All rights reserved. | |
// | |
#import "DTNetworkOperation.h" | |
static NSString * const kTaskStateKeyPath = @"state"; | |
static void * DTContext = &DTContext; | |
@interface DTNetworkOperation () | |
@property (nonatomic, readonly) NSURLSessionTask *task; | |
- (instancetype)initWithTask:(NSURLSessionTask *)task; | |
@end | |
@implementation DTNetworkOperation | |
+ (instancetype)operationWithTask:(NSURLSessionTask *)task | |
{ | |
return [[self alloc] initWithTask:task]; | |
} | |
- (instancetype)initWithTask:(NSURLSessionTask *)task | |
{ | |
if (task.state != NSURLSessionTaskStateSuspended) { | |
NSLog(@"This should not happen"); | |
return nil; | |
} | |
self = [super init]; | |
if (self) { | |
_task = task; | |
[_task addObserver:self | |
forKeyPath:kTaskStateKeyPath | |
options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew | |
context:DTContext]; | |
} | |
return self; | |
} | |
- (void)dealloc | |
{ | |
[_task removeObserver:self forKeyPath:kTaskStateKeyPath context:DTContext]; | |
} | |
- (void)execute | |
{ | |
NSURLSessionTask *task = self.task; | |
if (task.state != NSURLSessionTaskStateSuspended) { | |
assert(NO); | |
NSLog(@"This should not happen"); | |
return; | |
} | |
[task resume]; | |
} | |
- (void)observeValueForKeyPath:(NSString *)keyPath | |
ofObject:(id)object | |
change:(NSDictionary *)change | |
context:(void *)context | |
{ | |
id oldValue = change[NSKeyValueChangeOldKey]; | |
id newValue = change[NSKeyValueChangeNewKey]; | |
if (oldValue && newValue && [newValue isEqual:oldValue]) { | |
return; | |
} | |
if (context == DTContext && | |
object == self.task && | |
[keyPath isEqualToString:kTaskStateKeyPath] && | |
self.task.state == NSURLSessionTaskStateCompleted) { | |
[self finish]; | |
} | |
} | |
- (void)cancel | |
{ | |
[self.task cancel]; | |
[super cancel]; | |
} | |
@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
// | |
// DTOperation.h | |
// DTOperation | |
// | |
// Created by Diogo on 10/14/15. | |
// Copyright © 2015 Diogo Tridapalli. All rights reserved. | |
// | |
@import Foundation; | |
/** | |
* Operation states | |
*/ | |
typedef NS_ENUM(NSInteger, DTOperationState){ | |
/** | |
* Operation is ready to execute | |
*/ | |
kDTOperationStateReady = 0, | |
/** | |
* Operation is executing | |
*/ | |
kDTOperationStateExecuting, | |
/** | |
* Operation is finished | |
*/ | |
kDTOperationStateFinished, | |
/** | |
* Operation is finished due to cancel | |
*/ | |
kDTOperationStateCanceled | |
}; | |
/** | |
* Class to be subclassed for encapsulate a task in an operation | |
*/ | |
@interface DTOperation : NSOperation | |
/** | |
* Operation state | |
* | |
* @see DTOperationState | |
*/ | |
@property (readonly) DTOperationState state; | |
/** | |
* Methdos that must be overide on subclasses, the task should be started inside this method and | |
* finish shold be called on task completion | |
*/ | |
- (void)execute; | |
/** | |
* When the task is finished this method should be called | |
*/ | |
- (void)finish; | |
@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
// | |
// DTOperation.m | |
// DTOperation | |
// | |
// Created by Diogo on 10/14/15. | |
// Copyright © 2015 Diogo Tridapalli. All rights reserved. | |
// | |
#import "DTOperation.h" | |
static NSString * const kStateKeyPath = @"state"; | |
static inline BOOL IsValidStateTransition(DTOperationState fromState, | |
DTOperationState toState) | |
{ | |
if (fromState == toState) { | |
NSLog(@"Inconsistent state, from %ld to %ld", (long)fromState, (long)toState); | |
return NO; | |
} | |
switch (fromState) { | |
case kDTOperationStateReady: | |
case kDTOperationStateExecuting: | |
return toState > fromState; | |
case kDTOperationStateFinished: | |
case kDTOperationStateCanceled: | |
default: | |
return NO; | |
} | |
} | |
@interface DTOperation (){ | |
DTOperationState _state; | |
} | |
@property (readwrite) DTOperationState state; | |
@end | |
@implementation DTOperation | |
// use the KVO mechanism to indicate that changes to "state" affect other properties as well | |
+ (NSSet *)keyPathsForValuesAffectingIsReady | |
{ | |
return [NSSet setWithObject:kStateKeyPath]; | |
} | |
+ (NSSet *)keyPathsForValuesAffectingIsExecuting | |
{ | |
return [NSSet setWithObject:kStateKeyPath]; | |
} | |
+ (NSSet *)keyPathsForValuesAffectingIsFinished | |
{ | |
return [NSSet setWithObject:kStateKeyPath]; | |
} | |
+ (NSSet *)keyPathsForValuesAffectingIsCanceled | |
{ | |
return [NSSet setWithObject:kStateKeyPath]; | |
} | |
- (DTOperationState)state | |
{ | |
@synchronized(self) { | |
return _state; | |
} | |
} | |
- (void)setState:(DTOperationState)state | |
{ | |
@synchronized(self) { | |
if (NO == IsValidStateTransition(_state, state)) { | |
return; | |
} | |
[self willChangeValueForKey:kStateKeyPath]; | |
_state = state; | |
[self didChangeValueForKey:kStateKeyPath]; | |
} | |
} | |
+ (BOOL)automaticallyNotifiesObserversOfState | |
{ | |
return NO; | |
} | |
- (BOOL)isReady | |
{ | |
return self.state == kDTOperationStateReady && [super isReady]; | |
} | |
- (BOOL)isExecuting | |
{ | |
return self.state == kDTOperationStateExecuting; | |
} | |
- (BOOL)isFinished | |
{ | |
return self.state >= kDTOperationStateFinished; | |
} | |
- (BOOL)isCancelled | |
{ | |
return self.state == kDTOperationStateCanceled; | |
} | |
- (void)start | |
{ | |
if (self.state != kDTOperationStateReady) { | |
return; | |
} | |
self.state = kDTOperationStateExecuting; | |
[self execute]; | |
} | |
- (void)execute | |
{ | |
[self finish]; | |
} | |
- (void)cancel | |
{ | |
self.state = kDTOperationStateCanceled; | |
} | |
- (void)finish | |
{ | |
self.state = kDTOperationStateFinished; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment