Disclaimer: I am also not that 'expert' when it comes to blocks. But I have learned this method throughout my career as an iOS Developer. This is a combination of what I learned from my senior teammates.
By the way, I am using the AFNetworking Framework.
Set up...
1. Create a singleton-patterned APIClient class which is a subclass of AFHTTPClient.
2. Our APIClient interface file should look like this...
#import <Foundation/Foundation.h>
#import "AFHTTPClient.h"
@interface APIClient : AFHTTPClient
+ (APIClient *)sharedClient;
@end
3. And in our implementation file:
#import "APIClient.h"
#import "Constants.h"
@implementation GCAPIClient
/**
We usually have our Development and Production API links, right? So to cater the changes in our Development and Production URLs, let's do this #if #else #endif preprocessor directives.
DevelopmentAPIURL and ProductionAPIURL values are defined in my Constants.h file
Example in Constants.h file:
#define DevelopmentAPIURL @"https://jescdev.sample.com/api/"
#define ProductionAPIURL @"https://jesc.sample.com/api/"
*/
+ (APIClient *)sharedClient
{
static APIClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if STAGING
_sharedClient = [[APIClient alloc] initWithBaseURL:[NSURL URLWithString:DevelopmentAPIURL]];
#else
_sharedClient = [[APIClient alloc] initWithBaseURL:[NSURL URLWithString:ProductionAPIURL]];
#endif
});
return _sharedClient;
}
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
return self;
}
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {
NSMutableURLRequest *request = [super requestWithMethod:method path:path parameters:parameters];
[request setTimeoutInterval:120];
return request;
}
@end
4. Next, I will setup my RequestManager class which is an NSObject subclass. So in my RequestManager.h file:
#import <Foundation/Foundation.h>
#import "AFJSONRequestOperation.h"
typedef void(^ResultBlock)(id results);
typedef void(^FailedBlock)(NSError *error);
@interface RequestManager : NSObject {
AFJSONRequestOperation *operation;
}
//We'll get to know more of these later... These methods basically reports a success or fail back to the calling function right after the request has finished executing.
- (void)reportSuccess:(id)results;
- (void)reportFailure:(NSError *)error;
//We'll also get to know more of these later. These methods will just set a completionBlock if the request reported a Success or failureBlock if the request reported a Failure.
- (void)setCompletionBlock:(ResultBlock)aCompletionBlock;
- (void)setFailedBlock:(FailedBlock)aFailedBlock;
- (void)start; //To Start the request
- (void)cancel; // To Cancel the request. This is important so we can stop the request whenever we want to.
- (BOOL)isExecuting; //To know if the request is being executed or is currently executing
- (BOOL)isFinished; //To know if the request has finished executing
//Optional
- (void)showSuccessAlertViewWithSuccessMessage:(NSString *)successMessage;
- (void)showFailedAlertViewWithErrorMessage:(NSString *)errorMessage;
@end
//RequestManager.m file
#import "RequestManager.h"
#import "APIClient.h"
@implementation RequestManager {
ResultBlock completionBlock;
FailedBlock failureBlock;
}
#pragma mark - Methods
- (void)reportSuccess:(id)results
{
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(results);
});
}
}
- (void)reportFailure:(NSError *)error
{
if (failureBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
failureBlock(error);
});
}
}
#pragma mark - Block Setters
- (void)setCompletionBlock:(ResultBlock)aCompletionBlock
{
completionBlock = [aCompletionBlock copy];
}
- (void)setFailedBlock:(FailedBlock)aFailedBlock
{
failureBlock = [aFailedBlock copy];
}
#pragma mark - Public Methods
//Starts the operation.
- (void)start
{
[[APIClient sharedClient] enqueueHTTPRequestOperation:operation];
}
//The operation has been canceled.
- (void)cancel
{
[operation cancel];
}
//The operation is currently executing.
- (BOOL)isExecuting
{
return [operation isExecuting];
}
// The operation has finished executing.
- (BOOL)isFinished
{
return [operation isFinished];
}
#pragma mark - AlertViews
/**
Shows a Successful Message Alert
@param successMessage, a string with Success-related message
*/
- (void)showSuccessAlertViewWithSuccessMessage:(NSString *)successMessage
{
UIAlertView *alertError = [[UIAlertView alloc] initWithTitle:@"Success!" message:successMessage delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertError show];
}
/**
Shows a Failed Message Alert
@param errorMessage, a string with error message of what went wrong why the request failed
*/
- (void)showFailedAlertViewWithErrorMessage:(NSString *)errorMessage
{
UIAlertView *alertError = [[UIAlertView alloc] initWithTitle:@"Failed!" message:errorMessage delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertError show];
}
@end
5. The last thing that we are going to prepare is our ResultSet. What I mean by ResultSet is that what do we usually RECEIVE from the API? There are results that totally look the same, so we will just have to create an object of what the API usually returns.
So you have to decide what your API usually returns and just modify the list. You can always add or remove variables on the list -- make it as flexible as possible. A flexible and easy to reuse codes are GOOD codes ;-)
Currently, in my part, our API usually returns the following so this is how my ResultSet class currently look like:
#import <Foundation/Foundation.h>
@interface EventResultSet : NSObject
@property NSInteger currentPage;
@property NSInteger resultPageCount;
@property NSInteger resultCount;
@property (nonatomic, strong) NSString *noResultFoundText;
@property (nonatomic, strong) NSMutableArray *results;
@end
#import "EventResultSet.h"
@implementation EventResultSet
//Just in case you still didn't know, in the new versions of Xcode, there was no need for us to synthesize the properties anymore. Ain't this nice?
@end
Let the fun of calling API services using blocks begin! :D
Can we first imagine this scenario in our app? Most social applications will need to fetch FEEDS from their server. "Feeds" are the status messages/updates of our "friends" or the people we "follow" and even ourselves. So imagine we have to fetch Feeds from our server...You may opt to just create a Request class for all the API request functions that we will be making but I suggest we create request classes according to our modules for easier editing in the future.
6. Let's create a FeedRequest class which is a subclass of RequestManager (the one that we did above) for our Feeds module.
This is how my FeedRequest interface file looks like:
#import <Foundation/Foundation.h>
#import "RequestManager.h"
/**
A customized request class for fetching feeds/feed-related fetches.
*/
@interface FeedRequest : RequestManager
//Methods
- (void)feedsWithParameters:(NSMutableDictionary *)parameters; //We will know later what the parameters will be.
@end
7. And in my FeedRequest implementation file, I will implement the feedsWithParameters: method.
/**
Fetch list of events from the server. Increment currentPage in order to fetch more events.
@param parameters, a dictionary of parameters that may include currentPage or page, and pageSize or limit of results.
*/
- (void)feedsWithParameters:(NSMutableDictionary *)parameters
{
ResultSet *resultSet = [[ResultSet alloc] init];
if (! resultSet.results) {
resultSet.results = [[NSMutableArray alloc] init];
}
//Path is the string after our APIURL. From the API URL given above, if we get our list of feeds using the URL : https://jescdev.sample.com/api/feeds then our path value will be "feeds"
NSMutableURLRequest *request = [[GCAPIClient sharedClient] requestWithMethod:@"GET" path:@"feeds" parameters:parameters];
operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
//Assuming that the returned JSON looks like this. Do not include the texts on maroon to your code, this is just a sample response.
JSON: {
currentPage = 1;
list = (
{
status = Test
userId = 1234;
userName = JennCurativo;
...
},
{
status = Hello
userId = 1002;
userName = Polka;
...
},
...
{
status = Test 3
userId = 5688;
userName = Demitria;
...
}
);
pageCount = 1;
resultCount = 6;
}
//This is how we save the results:
resultSet.currentPage = [JSON[@"currentPage"] integerValue];
resultSet.resultCount = [JSON[@"resultCount"] integerValue];
resultSet.resultPageCount = [JSON[@"pageCount"] integerValue];
NSArray *feeds = [JSON[@"list"] allObjects];
for (id result in feeds) {
Feed *feed = [[Feed alloc] init];
//example on how to set feed values
//feed.status = result[@"status"];
[resultSet.results addObject:feed];
}
if ([feeds count] == 0) {
resultSet.noResultFoundText = JSON[@"message"];
}
//Report success back to the calling function
[self reportSuccess: resultSet];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
//Report failure back to the calling function
[self reportFailure:error];
//Optional
//[self showFailedAlertViewWithErrorMessage:error.localizedDescription];
}];
}
Let's call this API method that we just created from our ViewController.
8. Hey, I'm assuming you have your view controllers ready. So what I'm gonna do is just simply make a function in calling our API method. :D
Inside our view controller .m file, we make a function:
- (void)fetchFeedsFromServer
{
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
parameters[@"pageSize"] = [NSNumber numberWithInt:20];
parameters[@"page"] = [NSNumber numberWithInteger:++self.currentPage];
//Add any other parameters needed for your API request
FeedRequest *request = [[FeedRequest alloc] init];
[request feedsWithParameters:parameters];
[request setCompletionBlock:^(id results) {
//Remember the [self reportSuccess: resultSet]; code above (#7)? Upon reportingSuccess, it will go inside this block. resultSet is received by results
//Processing of fetched data.
ResultSet *resultSet = (ResultSet *)results;
self.resultPageCount = resultSet.resultPageCount;
self.currentPage = resultSet.currentPage;
for (int i = 0; i < [resultSet.results count]; i++) {
[self.feedsArray addObject:[resultSet.results objectAtIndex:i]];
}
//Update our feeds tableview
[self.tableView reloadData];
}];
[request setFailedBlock:^(NSError *error) {
//If the request fails, the [self reportFailure:error]; will go inside this block.
}];
[request start];
}
Hello Jann,
ReplyDeleteYour tutorial is very helpful for me....May i get code for this tutorial?
Your posts is really helpful for me.Thanks for your wonderful post.It is really very helpful for us and I have gathered some important information from this blog. so keep sharing..
ReplyDeleteIndustrial Architecture
Warehouse Architect
Civil Engineering Consultants
Office Interiors in Chennai
ReplyDeleteWhoa! I’m enjoying the template/theme of this website. It’s simple, yet effective. A lot of times it’s very hard to get that “perfect balance” between superb usability and visual appeal. I must say you’ve done a very good job with this.
AWS Training in Velachery | Best AWS Course in Velachery,Chennai
Best AWS Training in Chennai | AWS Training Institutes |Chennai,Velachery
Amazon Web Services Training in Anna Nagar, Chennai |Best AWS Training in Anna Nagar, Chennai
Amazon Web Services Training in OMR , Chennai | Best AWS Training in OMR,Chennai
Amazon Web Services Training in Tambaram, Chennai|Best AWS Training in Tambaram, Chennai
Thanks a lot for sharing us about this update. Hope you will not get tired on making posts as informative as this.
ReplyDeleteJava training in Chennai | Java training in Annanagar
Java training in Chennai | Java training institute in Chennai | Java course in Chennai
Java training in Chennai | Java training institute in Chennai | Java course in Chennai
Java training in Bangalore | Java training in Electronic city
This is beyond doubt a blog significant to follow. You’ve dig up a great deal to say about this topic, and so much awareness. I believe that you recognize how to construct people pay attention to what you have to pronounce, particularly with a concern that’s so vital. I am pleased to suggest this blog.
ReplyDeleteData Science course in Indira nagar
Data Science course in marathahalli
Data Science Interview questions and answers
Data science training in tambaram | Data Science Course in Chennai
Data Science course in btm layout | Data Science training in Bangalore
Data science course in kalyan nagar | Data Science Course in Bangalore
Great thoughts you got there, believe I may possibly try just some of it throughout my daily life.
ReplyDeleteexcel advanced excel training in bangalore | Devops Training in Chennai
Read all the information that i've given in above article. It'll give u the whole idea about it.
ReplyDeleteangularjs Training in marathahalli
angularjs interview questions and answers
angularjs Training in bangalore
angularjs Training in bangalore
angularjs Training in chennai
automation anywhere online Training
very nice information
ReplyDeletehttp://quincetravels.com
Thanks for giving great kind of information. So useful and practical for me. Thanks for your excellent blog, nice work keep it up thanks for sharing the knowledge.
ReplyDeleteAWS Training in Chennai | AWS Training Institute in Chennai
thanks for sharing this information
ReplyDeleteAmazon web services training in bangalore
best AWS Training institute in Bangalore
best training institute for data science in bangalore
best tableau training institutes in bangalore
best python training institute in bangalore
python training in jayanagar bangalore
Artificial Intelligence training in Bangalore
super blogs...!
ReplyDeleteinternship in chennai for ece students
internships in chennai for cse students 2019
Inplant training in chennai
internship for eee students
free internship in chennai
eee internship in chennai
internship for ece students in chennai
inplant training in bangalore for cse
inplant training in bangalore
ccna training in chennai
Excellent and very cool idea and great content of different kinds of the valuable information's.
ReplyDeleteBig Data Training Institute in Pune
Hadoop Training in Pune
Wow, amazing post! Really engaging, thank you.
ReplyDeleteTableau Training in Pune
Tableau Training Institutes in Pune
no deposit bonus forex 2021 - takipçi satın al - takipçi satın al - takipçi satın al - takipcialdim.com/tiktok-takipci-satin-al/ - instagram beğeni satın al - instagram beğeni satın al - google haritalara yer ekleme - btcturk - tiktok izlenme satın al - sms onay - youtube izlenme satın al - google haritalara yer ekleme - no deposit bonus forex 2021 - tiktok jeton hilesi - tiktok beğeni satın al - binance - takipçi satın al - uc satın al - finanspedia.com - sms onay - sms onay - tiktok takipçi satın al - tiktok beğeni satın al - twitter takipçi satın al - trend topic satın al - youtube abone satın al - instagram beğeni satın al - tiktok beğeni satın al - twitter takipçi satın al - trend topic satın al - youtube abone satın al - instagram beğeni satın al - tiktok takipçi satın al - tiktok beğeni satın al - twitter takipçi satın al - trend topic satın al - youtube abone satın al - instagram beğeni satın al - perde modelleri - instagram takipçi satın al - instagram takipçi satın al - cami avizesi - marsbahis
ReplyDeleteinstagram takipçi satın al
ReplyDeleteinstagram takipçi satın al
takipçi satın al
takipçi satın al
instagram takipçi satın al
takipçi satın al
instagram takipçi satın al
perde modelleri
ReplyDeletesms onay
Vodafone Mobil Ödeme Bozdurma
nft nasıl alınır
Ankara evden eve nakliyat
trafik sigortasi
Dedektor
web sitesi kurma
aşk kitapları
smm panel
ReplyDeletesmm panel
iş ilanları
instagram takipçi satın al
hırdavat
beyazesyateknikservisi.com.tr
servis
tiktok jeton hilesi