- Published on
Simple Objective-C Server Communication
- Authors
- Name
- Christopher Helf
Communicating to a server backend from your iOS app is required for almost any app nowadays, which is why I have decided to quickly write an article about how to achieve this easily – see also this link for further information. This is a quite simple way to do it, and should more or less serve as a starting point for how to do it.
There is an excellent library called AFNetworking which does most of the work for you. Go ahead and download the library first (or install it via Cocoapods). If you want to add the files manually, create for instance a new group in your XCode project called AFNetworking and simply drag & drop all the *.h and *.m files from the AFNetworking folder into this folder – choose to copy the files into the destination folder. This enables you to make use of the library within your project.
We will use function blocks for the success and failure responses. Create a new header file called “Definitions.h” in your project with the following content:
//
// Definitions.h
//
#ifndef Definitions_h
#define Definitions_h
#import <Foundation/Foundation.h>
typedef void (^CommunicationResponse)(NSInteger statusCode, NSString* error, NSDictionary* responseObject);
#endif
What we do here is very simply declare a type for the function block we are going to use. The first argument will be the statuscode of the server response, the second is a potential error message, the third the response-object from the server. We will know whether an error occurred or not when error is nil. You will see in use later – it’s pretty straightforward. In order for the definitions file to be globally available, we are going to add it to *.pch Prefix file found in the Supporting Files folder. Add the Definitions import under the obj-c definition symbol. It should look something like this
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "Definitions.h"
#endif
The Definitions.h file will from now on be available in all classes used within the project.
We will now create a Communication class responsible for the communication to a potential server API. Let’s create an objective-c class called “Communication”. Add the following to the header file:
//
// Communication.h
//
#import <Foundation/Foundation.h>
#import "AFNetworking.h"
typedef void (^CompletionBlock)(AFHTTPRequestOperation *operation, NSDictionary* responseObject);
typedef void (^FailureBlock)(AFHTTPRequestOperation *operation, NSError *error);
@interface Communication : NSObject
+(void) makeAPICallWithParams: (NSDictionary*) params ofType: (NSString*) type andResponse: (CommunicationResponse) responseHandler;
@end
First, we import the main file of AFNetworking in the header, so we have access to all the functions of the library. Next, we define two simple function blocks CompletionBlock and FailureBlock which we can use later in order to define our handlers in case the operations succeeds or fails. Next, we define a static method (+) so we don’t need to initialize our class. The function has three arguments: a NSDictionary of parameters which we want to send to the server, the request type we want to send (GET, POST, DELETE or PUT) and a function block which will be called when the operation is finished.
Next, add this to the implementation file:
//
// Communication.m
//
#import "Communication.h"
static NSString * const url = @"http://yoururlendpoint.com/api";
@implementation Communication
+(void) makeAPICallWithParams: (NSDictionary*) params ofType: (NSString*) type andResponse: (CommunicationResponse) responseHandler {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
FailureBlock errorHandler = ^(AFHTTPRequestOperation *operation, NSError *error) {
NSHTTPURLResponse* response = operation.response;
NSInteger statusCode = [response statusCode];
responseHandler(statusCode, [error localizedDescription], nil);
};
CompletionBlock completionHandler = ^(AFHTTPRequestOperation *operation, NSDictionary *responseObject) {
NSHTTPURLResponse* response = operation.response;
NSInteger statusCode = [response statusCode];
responseHandler(statusCode, nil, responseObject);
};
if([type isEqual: @"POST"]) {
[manager POST:url parameters:params success:completionHandler failure: errorHandler];
} else if([type isEqual: @"GET"]) {
[manager GET:url parameters:params success:completionHandler failure: errorHandler];
} else if([type isEqual: @"PUT"]) {
[manager PUT:url parameters:params success:completionHandler failure: errorHandler];
} else if([type isEqual: @"DELETE"]) {
[manager DELETE:url parameters:params success:completionHandler failure: errorHandler];
} else {
responseHandler(0, @"Request Type not recognized", nil);
}
}
@end
First, for the sake of simplicity, we declare the URL we want to use as a constant. This could for instance also be included as an argument of the function in case we have different endpoints. The implementation of the function is pretty straightforward: We first declare two function blocks, one in case the operation fails, and one in case the operation succeeds. In both cases the responseHandler is called, but with different arguments. If the operation succeeds, the error argument is nil, otherwise the response-object is nil. We then check which request type we want to use and call the AFNetworking’s method accordingly. Here is a simple example of how to use the Communication class:
NSDictionary *params = @{@"parameter1": @"parameter1", @"parameter2": @"parameter2"};
[Communication makeAPICallWithParams:params ofType:@"POST" andResponse:^(NSInteger statusCode, NSString* error, NSDictionary *responseObject) {
if(error) {
NSLog(@"Unsuccessful");
} else {
NSLog(@"Successfull");
}
}];
In a potential ViewController, we could, before we make the API call, open an indicator view as a subview, and then close it plus display an error message if the operation fails, or open another view when the operation succeeds.
Hope this helps for building apps that need server communication!