I'm new to iOS while I'm developing I am calling web services every where...I want like one single class for get,post,put methods...then call [self post];
[parameters:....]... that means I want to call single methods for all get services and post.. please help me...how it is..
NSURL *url = [NSURL URLWithString:#"https://example.com/"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
height, #"user[height]",
weight, #"user[weight]",
nil];
[httpClient postPath:#"/myobject" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *responseStr = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"Request Successful, response '%#'", responseStr);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"[HTTPClient Error]: %#", error.localizedDescription);
}];
For AFNetworking 2.0 (and also using the new NSDictionary syntax):
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *params = #{#"user[height]": height,
#"user[weight]": weight};
[manager POST:#"https://example.com/myobject" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
From here you can create singleton class - How to create singleton class in objective C
Then you need to create your own method with completion handler and call where you want.
Example: Post Method:
+(void)postWebserviceWithURL:(NSString*)webServiceURL withParam:(NSDictionary*)urlParameters withCompletion:(void(^)(NSDictionary*response))completion {
//Your code goes here
}
here is the related answer.You should create nsobject classes something like webservices and create the singleton instance object like this.
+(WebServices *)sharedInstance{
/* Use this to make it a singleton class */
if (sharedObj==Nil) {
sharedObj=[[WebServices alloc]init];
}
return sharedObj;
/**/
}
using this singleton instance you can call the methods what ever you required.
Eg: [[webservices sharedInstance] post];
With in the method you can use the required web services hits such as post,get and put.
use the Nsnotifiers to consume the responses from the API hits.
+ (instancetype)sharedInstance
{
static CustomClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//Override your constructor for custom initialization if you want
sharedInstance = [[CustomClass alloc] init];
});
return sharedInstance;
}
Then define your methods for CRUD actions.
simply access like
[[CustomClass sharedInstance] POST:...];
[[CustomClass sharedInstance] GET:...];
Related
I have a question on which is best way or the correct way to send AFNetworking results to controller. Is it via delegate or notification?
I created a class to handle make API calls that has the code below. So if imported this class to another controller and call this method to make API call. Should I do delegate or notification?
I have read www.raywenderlich.com/59255/afnetworking-2-0-tutorial and it is using delegates. I also been watched CodeSchool tutorial, which they used notification from Model to Controller.
I added the code below in a hope to better show my question.
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];
// notification way inside the BLOCK
[ manager GET:path parameters:params
success:^(NSURLSessionDataTask *operation, id responseObject) {
[ [NSNotificationCenter defaultCenter] postNotificationName:notificationName
object:nil
userInfo:responseObject ];
} failure:^(NSURLSessionDataTask *operation, NSError *error) {
[ [NSNotificationCenter defaultCenter] postNotificationName:notificationName
object:nil ];
}];
// delegate way inside the BLOCK
[ manager GET:path parameters:params
success:^(NSURLSessionDataTask *operation, id responseObject) {
if ([delegate respondsToSelector:#selector(getUserFeedsDidFinish:resultDict:)])
{
[delegate performSelector:#selector(getUserFeedsDidFinish:resultDict:) withObject:self withObject:resultDict];
}
} failure:^(NSURLSessionDataTask *operation, NSError *error) {
if ([delegate respondsToSelector:#selector(getUserFeeds:didFailWithResultDict:)]) {
[delegate performSelector:#selector(getUserFeeds:didFailWithResultDict:)
withObject:self
withObject:[NSDictionary dictionaryWithObject:error.userInfo forKey:KEY_ERRORS]];
}
}];
I will recommend use blocks, how? I will write a service for you, this one is wrote in a class called Connection:
+(void)requestLocation:(NSString*)googleReference completionBlock:(void (^)(NSString * coordinates, NSError * error)) handler{
NSString * urlString = #"https://maps.googleapis.com/maps/";
NSMutableDictionary * parametersDictionary = [NSMutableDictionary dictionary];
[parametersDictionary setObject:googleReference forKey:#"reference"];
[parametersDictionary setObject:#"true" forKey:#"sensor"];
[parametersDictionary setObject:#"key(it is not)" forKey:#"key"];
AFHTTPClient *HTTPClient = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:urlString]];
NSURLRequest *URLRequest = [HTTPClient requestWithMethod:#"GET" path:#"api/place/details/json" parameters:parametersDictionary];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:URLRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSError * error = nil;
NSDictionary * response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:&error];
NSDictionary * dicGeo = [((NSDictionary*)[response objectForKey:#"result"]) objectForKey:#"geometry"];
NSDictionary * coords = [dicGeo objectForKey:#"location"];
NSNumber * lat = [coords objectForKey:#"lat"];
NSNumber * lng = [coords objectForKey:#"lng"];
NSString * coordinates = [NSString stringWithFormat:#"%#,%#", lat.description, lng.description];
handler(coordinates, error);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
[requestOperation start];
}
Then to call this service:
[Connection requestLocation:#"google reference (it is not)" completionBlock:^(NSString *coordinates, NSError *error) {
//Your code with results.
}
I've only scratched the surface with AFNetworking. From what I've seen, most of it seems to use a third approach, blocks.
Blocks are somewhat new, and different than both delegates and notifications.
Blocks are an extension to C function pointers that let you pass code into a method when you call it.
A common design pattern using blocks is to create a method that takes a completion block. A completion block is a piece of code that gets invoked when an async request is completed.
Take the AFNewtworking method HTTPRequestOperationWithRequest as an example. That method takes a success block, that gets called if the request succeeds, and a failure block, that gets called if the request fails.
Block is the easiest way to use IMO. You don't need to implement extra delegate methods or you don't need any conformations.
Basically define your wrapper like this.
typedef void(^SampleRequestCompletion)(NSError *error, id data);
- (void)GET:(NSString *)URLString
parameters:(NSDictionary *)parameters
completion:(SampleRequestCompletion)completion
{
[self GET:URLString parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Do what you want
if (completion) {
completion(nil, data);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Failure case
if (completion) {
completion(error,nil);
}
}];
}
And call this method from any objects like this,
[self GET:path parameters:dictionary completion:^(NSError *error, id data) {
}];
So you can manage what to do whenever the call ends with success or failure.
As the tutorial recommended, we can extract the web service related code into a module which acts more like a model level thing. Considering the communication between the network module and views, view invoke/start the request on a singleton web service client, once response back the usual workflow would be send the result to view controller and show the data in the views. We don't need to return anything back to network module.
So this workflow is more like a notification than delegation. And set the V as the M's delegate, it's weird.
Notification : Hey, man, I have done my job, it's your turn.
Delegation: Hey, man, I have done lots, now I need you cover/back up/provide me some tasks, then I will continue/complete the work.
In some situations, it's difficult to choose which one better. For AFNetworking, I thought the Notification approach better.
I am using the below code below to perform my webservice calls with the service.I used AFNetworking version below 2.0 where AFHTTPClient .Now i migrated to latest version of AFNetworking .I donot find the AFHTTPClient class in the latest version . What should i replace with the curent code so that it works again .Any help please
#interface APIClient : AFHTTPClient
+ (APIClient*)client;
- (void)commandWithMethod:(NSString *)method params:(NSMutableDictionary*)params success:(APIClientSuccessCallback)successBlock failure:(APIClientFailureCallback)failureBlock;
#end
// Singleton method
+ (APIClient*)client {
static APIClient *client = nil;
static dispatch_once_t onceInst;
dispatch_once(&onceInst, ^{
client = [[self alloc] initWithBaseURL:[NSURL URLWithString:APIHost]];
[AFJSONRequestOperation addAcceptableContentTypes:[NSSet setWithObjects:
#"application/json",
#"text/json",
#"text/javascript",
#"text/plain",
#"text/html",
#"application/x-www-form-urlencoded", nil]];
});
return client;
}
#pragma mark - Init
// Intialize the API class with the destination host name
- (APIClient*)init {
self = [super init]; // call super init
if (self != nil) {
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
// Accept HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
[self setDefaultHeader:#"Accept" value:#"application/json"];
}
return self;
}
#pragma mark - Core API Methods
// This function sends an API call to the server
- (void)commandWithMethod:(NSString *)method params:(NSMutableDictionary*)params success:(APIClientSuccessCallback)successBlock failure:(APIClientFailureCallback)failureBlock {
[MBMNetworkActivity pushNetworkActivity];
NSMutableURLRequest *apiRequest = [self requestWithMethod:#"POST" path:method parameters:params];
AFJSONRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest: apiRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// success! :)
[MBMNetworkActivity popNetworkActivity];
successBlock(responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// failure! :(
[MBMNetworkActivity popNetworkActivity];
failureBlock(error);
}];
[operation start];
}
You can use NSURLSession for quite a bunch of the AFHTTPClient Stuff.
But to achieve all functionality just write a class like you now did but based on NSObject.
NSURLSession has a really nice API and great functionality combined with it.
AFHTTPRequestOperationManager is the replacement class to subclass instead of AFHTTPClient. It's not the same but it's probably what your looking for.
I would suggest you read Mattt Thompson's blog NSHipster. He is the author of AFNetworking and covered the changes a while back http://nshipster.com/afnetworking-2/. There is also an AFNetworking 2.0 migration guide https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-2.0-Migration-Guide that will be usefull to you.
Finally i was able to do with following changes replacing AFHttpClient with AFHTTPRequestOperationManager
typedef void (^APIClientSuccessCallback) (id response);
typedef void (^APIClientFailureCallback) (id error);
#interface APIClient : AFHTTPRequestOperationManager
+ (APIClient*)client;
- (void)commandWithMethod:(NSString *)method params:(NSMutableDictionary*)params success:(APIClientSuccessCallback)successBlock failure:(APIClientFailureCallback)failureBlock;
#end
#import "APIClient.h"
#implementation APIClient
+ (APIClient*)client {
static APIClient *client = nil;
static dispatch_once_t onceInst;
dispatch_once(&onceInst, ^{
client = [[self alloc] initWithBaseURL:[NSURL URLWithString:APIHost]];
client.responseSerializer = [AFJSONResponseSerializer serializer];
[client.responseSerializer setAcceptableContentTypes:[NSSet setWithObject:#"text/html"]];
});
return client;
}
#pragma mark - Core API Methods
// This function sends an API call to the server
- (void)commandWithMethod:(NSString *)method params:(NSMutableDictionary*)params success:(APIClientSuccessCallback)successBlock failure:(APIClientFailureCallback)failureBlock {
[self POST:method parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"response --- %#",responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error ----- %#",error);
}];
}
#end
I am trying to implement AFNetworking code to communicate with a web API. I am getting the following errors in the code:
No visible #interface for APIClass declares the selector
registerHTTPOperationClass
No visible #interface for APIClass declares the selector
setDefaultHeader:Value
No visible #interface for APIClass declares the selector
multiPartFormRequestWithMethod:path:parameters:constructingBodyWithblock
Obviously something to do with the new AFNetworking 2.0 migration...however I have been looking at all the migration posts and documentation and connot find the replacements for these without throwing an error:
// add the location details of the web service we wrote
#define kAPIHost #"http://myurl"
#define kAPIPath #"mywebapi/"
#implementation APIClass
// this is the implementation of the singleton method
+(APIClass*)sharedInstance{
static APIClass *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
sharedInstance = [[self alloc] initWithBaseURL:[NSURL URLWithString:kAPIHost]];
});
return sharedInstance;
}
-(APIClass*)init{
// call super init
self = [super init];
if (self != nil){
user = nil;
[self registerHTTPOperationClass:[AFHTTPRequestOperation class]];
// Accept HTTP header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
[self setDefaultHeader:#"Accept" value:#"application/json"];
}
return self;
}
// call to the server
-(void)commandWithParams:(NSMutableDictionary*)params onCompletion:
(JSONResponseBlock)completionBlock
{
// prepare e POST request by creating an NSMutableURLRequest instance using the
// parameters we want to send via POST
NSMutableURLRequest *apiRequest =
[self multipartFormRequestWithMethod:#"POST"
path:kAPIPath
parameters: params
constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
// attach file if needed
}];
// create an operation to handle the network communication in the background
// and intialize it with the POST request we just prepared
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:
apiRequest];
// now set the 2 blocks needed for success and failure
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id
responseObject)
{
// success! - if the call is successful then we just pass in the JSON response
NSLog(#"responseObject: %#", responseObject);
completionBlock(responseObject);
}
// if there is a failure in the network call then we call the failure block
// and contrcut a new dictinary to hold the message of the network error
failure:^(AFHTTPRequestOperation *operation, NSError * error) {
//failure!
completionBlock([NSDictionary dictionaryWithObject:[error localizedDescription]forKey:#"error"]);
}];
// at this point we can call the start method so that AFNetworking can do its
// magic in the background
[operation start];
}
#end
You're getting these errors because the methods you're calling aren't methods of whatever class you're subclassing. I'll assume you're subclassing AFHTTPSessionManager, which is recommended for iOS 7 in AFNetworking 2.0. Based on that...
For your first two errors, I believe the updated lines below are the AFNetworking 2.0 way of doing it with AFHTTPSessionManager:
-(APIClass*)init{
// call super init
self = [super init];
if (self != nil){
user = nil;
self.requestSerializer = [AFJSONRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
}
}
For your third error, the method multiPartFormRequestWithMethod:path:parameters:constructingBodyWithblock should be replaced with:
[self POST:kAPIPath parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
// attach file if needed
} success:^(NSURLSessionDataTask *task, id responseObject) {
// handle success
} failure:^(NSURLSessionDataTask *task, NSError *error) {
// handle failure
}];
I am new to iOS development.
I have an application which uses NSURLConnection methods for http transfers across the network. The application is using JSON classes SBJsonparser and SBJsonWriter classes for parsing the Json and Serialization protocols for coverting the objects to json and deserializing the specified dictionary into instace of objects and using Serialization properties. I have separate classes for each request to API which conforms to serializable protocol.
One of sample classes is as follows
+ (id) deserializeFromDictionary:(NSDictionary *)dictionary {
Class *obj = [super deserializeFromDictionary:dictionary];
return obj;
}
+ (NSArray *) serializableProperties {
static NSArray *properties = nil;
if (properties == nil) {
properties = [[NSArray alloc] initWithObjects:
[SerializableProperty propertyWithExternalName:#"username"
internalName:#"username" internalClass:[NSString class]],
[SerializableProperty propertyWithExternalName:#"pwd"
internalName:#"pwd" internalClass:[NSDate class]],
nil];
}
return properties;
}
- (NSDictionary *) serializeToDictionary {
NSMutableDictionary *userDictionary = [NSMutableDictionary
dictionaryWithDictionary:[super serializeToDictionary]];
return userDictionary;
}
Now I have to replace the whole architecture with AFNetworking
I have replaced the
[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
with the following AFNetworking.
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
/// validates and decodes JSON responses.
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// succes code
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// failure code
}];
// 5
[operation start];
My Question is, is this ok or do I need to change the serialization of objects also? Is it possible to replace the serialization protocol with any of the AFNetworking Classes?. If YES, please let me know how to change this.
Thanks in advance.
Your can use the following code.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:urlparameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Success Code
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Failure Code
}];
You can use GET/POST depending on your requirements. Using AFNetworking you don't need to handle JSON parsing manually.
I switched from AFnetworking to RestKit. In AFnetworking had an API class. The API.h class contained the following.
#import <UIKit/UIKit.h>
typedef void (^JSONResponseBlock)(NSDictionary* json);
#interface API : NSObject
//the authorized user
#property (strong, nonatomic) NSDictionary* user;
+(API*)sharedInstance;
//check whether there's an authorized user
//send an API command to the server
-(void)loginCommand:(NSMutableDictionary*)params onCompletion:(JSONResponseBlock)completionBlock;
And my API.m class looks like this.
+(API *)sharedInstance
{
static API *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^ {
sharedInstance = [[self alloc]initWithBaseURL:[NSURL URLWithString:kAPIHost]];
});
return sharedInstance;
}
#pragma mark - init
//intialize the API class with the destination host name
-(API *)init
{
//call super init
self = [super init];
if (self != nil){
//initialize the object
user = nil;
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
// Accept HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
[self setDefaultHeader:#"Accept" value:#"application/json"];
}
return self;
}
-(void)loginCommand:(NSMutableDictionary *)params onCompletion:(JSONResponseBlock)completionBlock{
NSLog(#"%#%#",kAPIHost,kAPILogin);
NSMutableURLRequest *apiRequest = [self multipartFormRequestWithMethod:#"POST" path:kAPILogin parameters:params constructingBodyWithBlock:^(id <AFMultipartFormData>formData){
//TODO: attach file if needed
}];
AFJSONRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest:apiRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
//success!
NSLog(#"SUCCESSSS!");
completionBlock(responseObject);
}failure:^(AFHTTPRequestOperation *operation, NSError *error){
//Failure
NSLog(#"FAILUREE!");
completionBlock([NSDictionary dictionaryWithObject:[error localizedDescription] forKey:#"error"]);
}];
[operation start];
}
Like you can see I only instantiate it once and put all my methods in over here. In my view controller I only need to call this method with a parameter-dictionary. Then I could read the whole JSON file.
Now with restKit I do this all on viewController level. I want to split it up like I did by AFNetworking. This is what I do in RestKit, At the moment is this all on viewController level.
//let AFNetworking manage the activity indicator
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
// Initialize HTTPClient
NSURL *baseURL = [NSURL URLWithString:#"http://virtuele-receptie.preview.sanmax.be"];
AFHTTPClient* client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
//we want to work with JSON-Data
[client setDefaultHeader:#"Accept" value:RKMIMETypeJSON];
// Initialize RestKit
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
//Do mapping
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dataMapping
pathPattern:nil
keyPath:#"data"
statusCodes:[NSIndexSet indexSetWithIndex:200]];
[objectManager addResponseDescriptor:responseDescriptor];
NSDictionary *dict = [[NSDictionary alloc]initWithObjectsAndKeys:_txtLogin.text,#"email",_txtPass.text,#"pwd", nil];
[objectManager getObject:nil path:#"/nl/webservice/company-user/login/apikey/key12345678" parameters:dict
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
//Success
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
//Failure
}];
So far with RestKit I haven't seen a huge need for an API class like you might create with other HTTP frameworks. RestKit has its own HTTP client (actually, just AFNetworking's client), so there's no need to have a class for your HTTP client, and I've found that each time I use the RKObjectManager I generally want access to the method parameters and block callbacks within each view controller. In other words, I don't want to run RestKit networking code in an API class, because I would essentially have to wrap the entire call in a method that can be accessed in the view controller (success block, failure block, etc).
In essence, RestKit's design lightens the networking code so much that in my experience with it - 3 or 4 apps now - I've yet to see enough reason write an API class like you describe.