I have an object of a connection class. This broadcasts different notifications to any object can register for via [NSNotificationCenter defaultCenter]
.
-(void) requestData
{
[[NSNotificationCenter defaultCenter] postNotificationName:kCuriculumDataSourceFetchingStarted object:nil];
[_sessionManager setDataTaskDidReceiveDataBlock:^(NSURLSession *session,
NSURLSessionDataTask *dataTask,
NSData *data)
{
if (dataTask.countOfBytesExpectedToReceive == NSURLSessionTransferSizeUnknown)
return;
NSUInteger code = [(NSHTTPURLResponse *)dataTask.response statusCode];
if (!(code> 199 && code < 400))
return;
long long bytesReceived = [dataTask countOfBytesReceived];
long long bytesTotal = [dataTask countOfBytesExpectedToReceive];
NSDictionary *progress = @{@"bytesReceived": @(bytesReceived),
@"bytesTotal": @(bytesTotal)};
[[NSNotificationCenter defaultCenter] postNotificationName:kCuriculumDataSourceProgress object:nil userInfo:progress];
}];
[self.sessionManager GET:@"recipient/"
parameters:nil
success:^(NSURLSessionDataTask *task, id responseObject)
{
[[NSNotificationCenter defaultCenter] postNotificationName:kCuriculumDataSourceFetchingSucceeded
object:nil
userInfo:@{@"response": responseObject}];
}
failure:^(NSURLSessionDataTask *task, NSError *error)
{
NSUInteger code = [(NSHTTPURLResponse *)task.response statusCode];
NSString *msg;
switch (code) {
case kCuriculumDataSourceFetchErrorAPIKeyNotFound: msg = @"Api Key not found or revoked"; break;
case kCuriculumDataSourceFetchErrorServerDown: msg = @"Server Down"; break;
default: msg = [error localizedDescription]; break;
}
[[NSNotificationCenter defaultCenter] postNotificationName:kCuriculumDataSourceFetchingFailed
object:nil
userInfo:@{@"error": msg}];
}];
}
An object that would write the received data to Core Data would register for kCuriculumDataSourceFetchingSucceeded
and can access the received response via notification.userInfo[@"response"]
.
The ViewController would register for kCuriculumDataSourceFetchingSucceeded
, kCuriculumDataSourceFetchingFailed
and kCuriculumDataSourceProgress
.
I only instantiate one object, I don't have to bother, if it is an singleton or not. And therefor I don't have to subclass it or do some associated object tricks to get an method that would return the singleton object. Classes interested in the data fetched from network will just listen for the notification — they don't have to know the object that fetched the data and they don't have to know if it is the only of its kind.
The connection class object itself can register to a notification that other classes would post to trigger a new data fetch.
A view controller could register for notifications like
-(void)configure
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fetchingStarted:) name:kCuriculumDataSourceFetchingStarted object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fetchingSucceeded:) name:kCuriculumDataSourceFetchingSucceeded object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fetchingFailed:) name:kCuriculumDataSourceFetchingFailed object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataSourceProgress:) name:kCuriculumDataSourceProgress object:nil];
}
In this case the view controller and the network controller import the same configuration header file that defines the tokens like kCuriculumDataSourceFetchingSucceeded
. But as these are plain NSStrings, even this dependency can be avoided easily.
An example for a view controllers method that will handle a notification
-(void)dataSourceProgress:(NSNotification *)notification
{
float bytesReceived = (float)[notification.userInfo[@"bytesReceived"] longLongValue];
float bytesTotal = (float)[notification.userInfo[@"bytesTotal"] longLongValue];
float progress = bytesReceived / bytesTotal;
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView.progress = progress;
self.imgView.layer.mask = self.progressView.layer;
});
}