Created
June 10, 2013 09:33
-
-
Save HHuckebein/5747552 to your computer and use it in GitHub Desktop.
A pattern for implementing a class managing a NSOperationQueue and NSOperations. The files attached show how to apply this pattern to downloading gists/gravatar images from github.com
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
// | |
// GistCommunicationManager.h | |
// GistExplorer | |
// | |
// Created by Bernd Rabe on 28.05.13. | |
// Copyright (c) 2013 RABE_IT Services. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import "OperationDelegate.h" | |
#import "ProviderDelegate.h" | |
@interface GistCommunicationManager : NSObject | |
+ (instancetype)sharedManger; | |
- (void)loadContentForProvider:(id <ProviderDelegate>)provider success:(void(^)())sucess failure:(OperationFailure)failure; | |
- (void)loadGravatarForProvider:(id <ProviderDelegate>)provider avatarURL:(NSURL *)avatarURL success:(OperationSuccessWithData)success; | |
- (void)cancelAllOperationsForProvider:(id)provider; | |
- (void)cancelOperationForKey:(NSString *)key; | |
@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
// | |
// GistCommunicationManager.m | |
// GistExplorer | |
// | |
// Created by Bernd Rabe on 28.05.13. | |
// Copyright (c) 2013 RABE_IT Services. All rights reserved. | |
// | |
#import "GistCommunicationManager.h" | |
#import "JSONOperation.h" | |
#import "GravatarOperation.h" | |
@interface GistCommunicationManager() | |
@property (nonatomic, strong) NSOperationQueue *operationQueue; | |
@property (nonatomic, strong) NSMutableDictionary *operations; | |
@end | |
@implementation GistCommunicationManager | |
+ (instancetype)sharedManger | |
{ | |
static GistCommunicationManager *sharedManger = nil; | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
sharedManger = [[GistCommunicationManager alloc] init]; | |
}); | |
return sharedManger; | |
} | |
- (NSOperationQueue *)operationQueue | |
{ | |
if (nil == _operationQueue) { | |
_operationQueue = [[NSOperationQueue alloc] init]; | |
_operationQueue.name = @"Background Processing Queue"; | |
} | |
return _operationQueue; | |
} | |
- (NSMutableDictionary *)operations | |
{ | |
if (nil == _operations) { | |
_operations = [NSMutableDictionary dictionary]; | |
} | |
return _operations; | |
} | |
- (void)loadContentForProvider:(id <ProviderDelegate>)provider success:(void(^)())sucess failure:(OperationFailure)failure | |
{ | |
JSONOperation *operation = [JSONOperation jsonOperationForProvider:provider | |
success:^(NSOperation *operation, NSString *objectNotation) { | |
[self.operations removeObjectForKey:[(JSONOperation *)operation operationKey]]; | |
NSError *error = nil; | |
[provider processData:objectNotation error:&error]; | |
if (nil == error) { | |
if (sucess) { | |
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ | |
sucess(); | |
}]; | |
} | |
} | |
else { | |
if(failure) { | |
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ | |
failure(operation, error); | |
}]; | |
} | |
} | |
} | |
failure:failure]; | |
[self.operations setObject:operation forKey:[operation operationKey]]; | |
[self.operationQueue addOperation:operation]; | |
} | |
- (void)loadGravatarForProvider:(id <ProviderDelegate>)provider avatarURL:(NSURL *)avatarURL success:(OperationSuccessWithData)success | |
{ | |
GravatarOperation *operation = [GravatarOperation gravatarOperationForProvider:provider | |
avatarURL:avatarURL | |
success:^(NSOperation *operation, NSData *data) { | |
[self.operations removeObjectForKey:[(GravatarOperation *)operation operationKey]]; | |
if (success) { | |
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ | |
success(operation, data); | |
}]; | |
} | |
}]; | |
[self.operations setObject:operation forKey:[operation operationKey]]; | |
[self.operationQueue addOperation:operation]; | |
} | |
- (void)cancelAllOperationsForProvider:(id)provider | |
{ | |
[self.operationQueue cancelAllOperations]; | |
NSMutableArray *keys = [NSMutableArray array]; | |
[self.operations enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { | |
if ([obj provider] == provider) { | |
[obj cancelOperation]; | |
[keys addObject:key]; | |
} | |
}]; | |
[self.operations removeObjectsForKeys:keys]; | |
} | |
- (void)cancelOperationForKey:(NSString *)key | |
{ | |
id operation = self.operations[key]; | |
if (operation && [operation conformsToProtocol:@protocol(OperationDelegate)]) { | |
[operation cancelOperation]; | |
[self.operations removeObjectForKey:key]; | |
} | |
} | |
@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
// | |
// GistProvider.h | |
// GistExplorer | |
// | |
// Created by Bernd Rabe on 28.05.13. | |
// Copyright (c) 2013 RABE_IT Services. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import "ProviderDelegate.h" | |
extern NSString *const gistsCellReuseIdentifier; | |
extern NSString *const GistsProviderErrorDomain; | |
extern NSString *const GistsProviderDidSelectGistNotification; | |
NS_ENUM(NSUInteger, GistsProviderError) { | |
GistsProviderInvalidJSONError | |
}; | |
@interface GistsProvider : NSObject <UITableViewDataSource, UITableViewDelegate, ProviderDelegate> | |
- (id)initWithURl:(NSURL *)url; | |
@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
// | |
// GistProvider.m | |
// GistExplorer | |
// | |
// Created by Bernd Rabe on 28.05.13. | |
// Copyright (c) 2013 RABE_IT Services. All rights reserved. | |
// | |
#import "GistsProvider.h" | |
#import "Gist.h" | |
#import "User.h" | |
#import "GistCommunicationManager.h" | |
NSString *const gistsCellReuseIdentifier = @"SubtitleStyle"; | |
NSString *const GistsProviderErrorDomain = @"GistsProviderErrorDomain"; | |
NSString *const GistsProviderDidSelectGistNotification = @"GistsProviderDidSelectGistNotification"; | |
@interface GistsProvider() | |
@property (nonatomic, strong) NSArray *data; | |
@property (nonatomic, strong) NSURL *contentURL; | |
@property (nonatomic, strong) UIImage *johnDoeImage; | |
@property (nonatomic, strong) NSCache *avatarImageCache; | |
@end | |
@implementation GistsProvider | |
- (id)initWithURl:(NSURL *)url | |
{ | |
self = [super init]; | |
if (self) { | |
self.contentURL = url; | |
} | |
return self; | |
} | |
#pragma mark - DataSource Part | |
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section | |
{ | |
NSParameterAssert(section == 0); | |
return self.data.count; | |
} | |
- (Gist *)gistForIndexPath:(NSIndexPath *)indexPath | |
{ | |
return self.data[indexPath.row]; | |
} | |
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
NSParameterAssert(indexPath.section == 0); | |
NSParameterAssert(indexPath.row < self.data.count); | |
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:gistsCellReuseIdentifier forIndexPath:indexPath]; | |
Gist *gist = [self gistForIndexPath:indexPath]; | |
cell.textLabel.text = [gist titleText]; | |
cell.detailTextLabel.text = [gist subtitleText]; | |
if ([gist hasUser]) { | |
NSData *gravatarData = [self.avatarImageCache objectForKey:gist.user.gravatarID]; | |
if (gravatarData) { | |
cell.imageView.image = [UIImage imageWithData:gravatarData]; | |
} | |
else { | |
__weak typeof(self) weakSelf = self; | |
[[GistCommunicationManager sharedManger] loadGravatarForProvider:(GistsProvider *)tableView.dataSource | |
avatarURL:gist.user.avatarURL | |
success:^(NSOperation *operation, NSData *data) { | |
[weakSelf.avatarImageCache setObject:data forKey:gist.user.gravatarID]; | |
UIImage *image = [UIImage imageWithData:data]; | |
[tableView cellForRowAtIndexPath:indexPath].imageView.image = image; | |
}]; | |
cell.imageView.image = self.johnDoeImage; | |
} | |
} | |
return cell; | |
} | |
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
NSString *operationKey = [[self gistForIndexPath:indexPath] operationKey]; | |
if (operationKey) { | |
[[GistCommunicationManager sharedManger] cancelOperationForKey:operationKey]; | |
} | |
} | |
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (indexPath.row % 2 == 0) { | |
cell.backgroundColor = [UIColor colorWithRed:163/255. green:204/255. blue:20/255. alpha:1.]; | |
} | |
} | |
#pragma mark - Delegate Part | |
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; | |
{ | |
Gist *gist = [self gistForIndexPath:indexPath]; | |
NSNotification *notification = [NSNotification notificationWithName:GistsProviderDidSelectGistNotification object:gist]; | |
[[NSNotificationCenter defaultCenter] postNotification:notification]; | |
} | |
#pragma mark - Lazy Init | |
- (UIImage *)johnDoeImage | |
{ | |
if (nil == _johnDoeImage) { | |
_johnDoeImage = [UIImage imageNamed:@"JohnDoe.png"]; | |
} | |
return _johnDoeImage; | |
} | |
- (NSCache *)avatarImageCache | |
{ | |
if (nil == _avatarImageCache) { | |
_avatarImageCache = [[NSCache alloc] init]; | |
} | |
return _avatarImageCache; | |
} | |
#pragma mark - ProviderDelegate | |
- (NSString *)title | |
{ | |
return @"Public Gist's"; | |
} | |
- (void)processData:(NSString *)objectNotation error:(NSError **)error | |
{ | |
NSData *unicodeNotation = [objectNotation dataUsingEncoding:NSUTF8StringEncoding]; | |
NSError *localError = nil; | |
id jsonObject = [NSJSONSerialization JSONObjectWithData:unicodeNotation | |
options:NSJSONReadingAllowFragments | |
error:&localError]; | |
NSDictionary *parsedObject = (id)jsonObject; | |
if (nil == parsedObject) { | |
if (error != NULL) { | |
*error = [NSError errorWithDomain:GistsProviderErrorDomain code:GistsProviderInvalidJSONError userInfo:nil]; | |
} | |
} | |
else { | |
NSMutableArray *gistsArray = [NSMutableArray array]; | |
for (NSDictionary *item in parsedObject) { | |
Gist *gist = [[Gist alloc] init]; | |
if (item[@"id"]) { | |
gist.gistID = [item[@"id"] integerValue]; | |
} | |
if (item[@"created_at"]) { | |
gist.createdAt = [gist dateFromISO8601String:item[@"created_at"]]; | |
} | |
if (item[@"description"] && item[@"description"] != [NSNull null]) { | |
gist.descriptionText = item[@"description"]; | |
} | |
if (item[@"url"]) { | |
gist.jsonURL = [NSURL URLWithString:item[@"url"]]; | |
} | |
if (item[@"user"] != [NSNull null]) { | |
gist.user = [User userFromUserDictionary:item[@"user"]]; | |
} | |
[gistsArray addObject:gist]; | |
} | |
self.data = [gistsArray copy]; | |
} | |
} | |
- (void)removeAllData | |
{ | |
_data = nil; | |
} | |
@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
// | |
// GravatarOperation.h | |
// GistExplorer | |
// | |
// Created by Bernd Rabe on 28.05.13. | |
// Copyright (c) 2013 RABE_IT Services. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import "OperationDelegate.h" | |
@interface GravatarOperation : NSOperation <NSURLConnectionDataDelegate, OperationDelegate> | |
+ (instancetype)gravatarOperationForProvider:(id)provider avatarURL:(NSURL *)avatarURL success:(OperationSuccessWithData)success; | |
@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
// | |
// GravatarOperation.m | |
// GistExplorer | |
// | |
// Created by Bernd Rabe on 28.05.13. | |
// Copyright (c) 2013 RABE_IT Services. All rights reserved. | |
// | |
#import "GravatarOperation.h" | |
#import "ProviderDelegate.h" | |
@interface GravatarOperation() | |
@property (nonatomic, assign) BOOL executing; | |
@property (nonatomic, assign) BOOL finished; | |
@property (nonatomic, assign) BOOL cancelled; | |
@property (nonatomic, strong) NSURL *url; | |
@property (nonatomic, weak) id <ProviderDelegate> delegate; | |
@property (nonatomic, strong) NSURLConnection *connection; | |
@property (nonatomic, strong) NSMutableData *receivedData; | |
@property (nonatomic, copy) OperationSuccessWithData success; | |
@end | |
@implementation GravatarOperation | |
- (void)start | |
{ | |
if ([self isCancelled]) | |
{ | |
[self willChangeValueForKey:@"isFinished"]; | |
_finished = YES; | |
[self didChangeValueForKey:@"isFinished"]; | |
return; | |
} | |
NSURLRequest *request = [NSURLRequest requestWithURL:self.url]; | |
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; | |
[self.connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; | |
[self.connection start]; | |
} | |
+ (instancetype)gravatarOperationForProvider:(id)provider avatarURL:(NSURL *)avatarURL success:(OperationSuccessWithData)success | |
{ | |
NSAssert(nil != provider, @"provider parameter can't be nil"); | |
GravatarOperation *operation = [[[self class] alloc] init]; | |
operation.delegate = provider; | |
operation.url = avatarURL; | |
operation.success = success; | |
return operation; | |
} | |
- (BOOL)isConcurrent { | |
return YES; | |
} | |
- (BOOL)isExecuting { | |
return _executing; | |
} | |
- (BOOL)isFinished { | |
return _finished; | |
} | |
- (BOOL)isCancelled { | |
return _cancelled; | |
} | |
- (NSMutableData *)receivedData | |
{ | |
if (nil == _receivedData) { | |
_receivedData = [NSMutableData data]; | |
} | |
return _receivedData; | |
} | |
#pragma mark - NSURLConnectionDataDelegate | |
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { | |
if (_success) { | |
self.success(self, [self.receivedData copy]); | |
} | |
[self willChangeValueForKey:@"isFinished"]; | |
[self willChangeValueForKey:@"isExecuting"]; | |
_executing = NO; | |
_finished = YES; | |
[self didChangeValueForKey:@"isExecuting"]; | |
[self didChangeValueForKey:@"isFinished"]; | |
} | |
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { | |
[self.receivedData appendData:data]; | |
} | |
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { | |
[connection cancel]; | |
_connection = nil; | |
_receivedData = nil; | |
_url = nil; | |
[self willChangeValueForKey:@"isFinished"]; | |
[self willChangeValueForKey:@"isExecuting"]; | |
_executing = NO; | |
_finished = YES; | |
[self didChangeValueForKey:@"isExecuting"]; | |
[self didChangeValueForKey:@"isFinished"]; | |
} | |
#pragma mark - OperationDelegate | |
- (NSString *)operationKey | |
{ | |
return [self.url absoluteString]; | |
} | |
- (id)provider | |
{ | |
return _delegate; | |
} | |
- (void)cancelOperation | |
{ | |
_success = nil; | |
[self.connection cancel]; | |
_connection = nil; | |
_receivedData = nil; | |
_url = nil; | |
[self willChangeValueForKey:@"isCancelled"]; | |
[self willChangeValueForKey:@"isFinished"]; | |
[self willChangeValueForKey:@"isExecuting"]; | |
_executing = NO; | |
_finished = YES; | |
_cancelled = YES; | |
[self didChangeValueForKey:@"isCancelled"]; | |
[self didChangeValueForKey:@"isExecuting"]; | |
[self didChangeValueForKey:@"isFinished"]; | |
} | |
@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
// | |
// JSONOperation.h | |
// GistExplorer | |
// | |
// Created by Bernd Rabe on 28.05.13. | |
// Copyright (c) 2013 RABE_IT Services. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import "OperationDelegate.h" | |
@interface JSONOperation : NSOperation <NSURLConnectionDataDelegate, OperationDelegate> | |
+ (instancetype)jsonOperationForProvider:(id)provider success:(OperationSuccessWithString)success failure:(OperationFailure)failure; | |
@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
// | |
// JSONOperation.m | |
// GistExplorer | |
// | |
// Created by Bernd Rabe on 28.05.13. | |
// Copyright (c) 2013 RABE_IT Services. All rights reserved. | |
// | |
#import "JSONOperation.h" | |
#import "ProviderDelegate.h" | |
@interface JSONOperation() | |
@property (nonatomic, assign) BOOL executing; | |
@property (nonatomic, assign) BOOL finished; | |
@property (nonatomic, assign) BOOL cancelled; | |
@property (nonatomic, strong) NSURL *url; | |
@property (nonatomic, weak) id <ProviderDelegate> delegate; | |
@property (nonatomic, strong) NSURLConnection *connection; | |
@property (nonatomic, strong) NSMutableData *receivedData; | |
@property (nonatomic, copy) OperationFailure failure; | |
@property (nonatomic, copy) OperationSuccessWithString success; | |
@end | |
@implementation JSONOperation | |
- (void)start | |
{ | |
if ([self isCancelled]) | |
{ | |
[self willChangeValueForKey:@"isFinished"]; | |
_finished = YES; | |
[self didChangeValueForKey:@"isFinished"]; | |
return; | |
} | |
NSURLRequest *request = [NSURLRequest requestWithURL:self.url]; | |
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; | |
[self.connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; | |
[self.connection start]; | |
[self willChangeValueForKey:@"isExecuting"]; | |
_executing = YES; | |
[self didChangeValueForKey:@"isExecuting"]; | |
} | |
- (NSMutableData *)receivedData | |
{ | |
if (nil == _receivedData) { | |
_receivedData = [NSMutableData data]; | |
} | |
return _receivedData; | |
} | |
+ (instancetype)jsonOperationForProvider:(id <ProviderDelegate>)provider success:(OperationSuccessWithString)success failure:(OperationFailure)failure | |
{ | |
NSAssert(nil != provider, @"provider parameter can't be nil"); | |
JSONOperation *operation = [[[self class] alloc] init]; | |
operation.delegate = provider; | |
operation.url = provider.contentURL; | |
operation.failure = failure; | |
operation.success = success; | |
return operation; | |
} | |
- (BOOL)isConcurrent { | |
return YES; | |
} | |
- (BOOL)isExecuting { | |
return _executing; | |
} | |
- (BOOL)isFinished { | |
return _finished; | |
} | |
- (BOOL)isCancelled { | |
return _cancelled; | |
} | |
#pragma mark - NSURLConnectionDataDelegate | |
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { | |
if (_success) { | |
NSString *receivedText = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding]; | |
_receivedData = nil; | |
self.success(self, receivedText); | |
} | |
[self willChangeValueForKey:@"isFinished"]; | |
[self willChangeValueForKey:@"isExecuting"]; | |
_executing = NO; | |
_finished = YES; | |
[self didChangeValueForKey:@"isExecuting"]; | |
[self didChangeValueForKey:@"isFinished"]; | |
} | |
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { | |
[self.receivedData appendData:data]; | |
} | |
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error | |
{ | |
[connection cancel]; | |
_connection = nil; | |
_receivedData = nil; | |
_url = nil; | |
if (_failure) { | |
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ | |
self.failure(self, error); | |
}]; | |
} | |
[self willChangeValueForKey:@"isFinished"]; | |
[self willChangeValueForKey:@"isExecuting"]; | |
_executing = NO; | |
_finished = YES; | |
[self didChangeValueForKey:@"isExecuting"]; | |
[self didChangeValueForKey:@"isFinished"]; | |
} | |
#pragma mark - OperationDelegate | |
- (NSString *)operationKey | |
{ | |
return [self.url absoluteString]; | |
} | |
- (id)provider | |
{ | |
return _delegate; | |
} | |
- (void)cancelOperation | |
{ | |
_failure = nil; | |
_success = nil; | |
[self.connection cancel]; | |
_connection = nil; | |
_receivedData = nil; | |
_url = nil; | |
[self willChangeValueForKey:@"isCancelled"]; | |
[self willChangeValueForKey:@"isFinished"]; | |
[self willChangeValueForKey:@"isExecuting"]; | |
_executing = NO; | |
_finished = YES; | |
_cancelled = YES; | |
[self didChangeValueForKey:@"isCancelled"]; | |
[self didChangeValueForKey:@"isExecuting"]; | |
[self didChangeValueForKey:@"isFinished"]; | |
} | |
@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
// | |
// OperationDelegate.h | |
// GistExplorer | |
// | |
// Created by Bernd Rabe on 28.05.13. | |
// Copyright (c) 2013 RABE_IT Services. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
typedef void(^OperationSuccessWithData)(NSOperation *operation, NSData *data); | |
typedef void(^OperationSuccessWithString)(NSOperation *operation, NSString *objectNotation); | |
typedef void(^OperationFailure)(NSOperation *operation, NSError *error); | |
@protocol OperationDelegate <NSObject> | |
- (id)provider; | |
- (NSString *)operationKey; | |
- (void)cancelOperation; | |
@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
// | |
// ProviderDelegate.h | |
// GistExplorer | |
// | |
// Created by Bernd Rabe on 28.05.13. | |
// Copyright (c) 2013 RABE_IT Services. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
@protocol ProviderDelegate <NSObject> | |
- (NSString *)title; | |
- (NSURL *)contentURL; | |
- (void)processData:(NSString *)objectNotation error:(NSError **)error; | |
- (void)removeAllData; | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment