I load some data from from my webservice via a method using blocks and callbacks.
From my ViewController
I make a request to the data loading class and pass the callback block:
- (void)loadRadioStationList
{
[self.radioDataLoader getRadioStationList:^(NSArray *data, NSDictionary *dictData){
self.alphabetizedDictionary = [[NSDictionary alloc] initWithDictionary:dictData];
self.sectionIndexTitles = [[NSArray alloc] initWithArray:data];
[self.radioStationTableView reloadData];
}];
}
Then in the dataloading class the method is as so:
-(void) getRadioStationList:(void (^)(NSArray *data, NSDictionary *dictData))callback
{
NSString *string = RadioStationListURL;
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFXMLParserResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSXMLParser *XMLParser = (NSXMLParser *)responseObject;
[XMLParser setShouldProcessNamespaces:YES];
XMLParser.delegate = self;
[XMLParser parse];
callback((NSArray*)self.sectionIndexTitles, (NSDictionary*)self.alphabetizedDictionary);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Radio List"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alertView show];
}];
[operation start];
}
It loads the XML file and parses out the data and send the data back to my VC via the callback block.
I've a couple of questions:
- Is this an acceptable way of doing this? Would you prefer to see it done another (better) way?
- If I was to write an API, would passing the data from the API back to the calling class be a good way to work it, or is there a more suitable method for an API?
1 Answer 1
Here is a way how I do it:
Create a class and name it like ClientApi
. This class should be a singleton link and be absolutely independent. Also, create a class APIRouter
which will return a path for a request. I'm using the AFNetworking framework for communication with different API etc. and JSONModel framework for mapping. Here you can find the full answer about network architecture in iOS apps.
@interface SFAPIClient ()
@property (nonatomic, strong) AFHTTPSessionManager *manager;
@end
@implementation SFAPIClient
+ (instancetype)sharedInstance
{
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[[self class] alloc] init];
});
return _sharedInstance;
}
If a request should return some data, pass block as a parameter to the method:
- (void)getAllChildsWithCompletion:(void(^)(NSArray *arr, NSError *error))completion
{
NSString *url = [SFAPIRouter childRoute];
[self setupRequestSerializer];
[self.manager GET:url parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSError *error = nil;
NSArray *arr = //map respons
if (!error)//if everything good call block with data
completion(arr, nil);
else
completion(nil, error);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
{
if (completion)
completion(nil, error);
}];
}
APIRouter:
static NSString * const kSFServerURL = @"http://0.0.0.0/api/v1";
@implementation SFAPIRouter
+ (NSString *)saveMediaToListRoute
{
return [kSFServerURL stringByAppendingString:@"/childs"];
}
And then in controller call function from your ClientApi
class:
//show activity indicator
[[SFAPIClient sharedInstance] getAllChildsWithCompletion:^(NSArray *arr, NSError *error){
//stop showing activity indicator
if (error)
{
//show alert
return;
}
//do what you need
}];