I want to implement the following: Have a custom class(so i can call/use it from many parts of my app) where with the help of AFNetworking,i request to the server and get the response.
My code inside the class works,but when i call it from other parts of my app,i am getting a null value. I believe it because of my custom class.Anyway here is my code:
UDIDGen.h
#import <Foundation/NSString.h>
#interface NSString (UDIDGen)
+ (NSString *)getUDID;
#end
UDIDGen.m
#import "UDIDGen.h"
#import "AFHTTPClient.h"
#import "AFHTTPRequestOperation.h"
#implementation NSString (UDIDGen)
+ (NSString *)getUDID{
__strong __block NSString *holder;
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://domain.com/id.php"]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET"
path:#"http://domain.com/id.php"
parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
holder=[NSString stringWithFormat:#"%#",[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]];
NSLog(#"SERVER RESPONSE: %#",holder);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[operation start];
return holder;
}
#end
And then when i want to call it from other parts of my app i
#import "UDIDGen.h"
and then
NSString *wsa=[NSString getUDID];
NSLog(#"Response %#",wsa);
For some reasong NSLog(#"SERVER RESPONSE: %#",holder); works and i am able to nslog the server response. But NSString *wsa=[NSString getUDID]; gives me null value.
I believe it has to do with the way i am implementing my custom class, i am doing something wrong.
Any help?
EDIT:My working code so other will use it:
UDIDGen.h
#import <Foundation/NSString.h>
#interface UDIDGen:NSObject{
}
- (void)getUDIDWithCompletion:(void (^)(NSString *udid))completion;
#end
UDIDGen.m
#import "UDIDGen.h"
#import "AFHTTPClient.h"
#import "AFHTTPRequestOperation.h"
#implementation UDIDGen
- (void)getUDIDWithCompletion:(void (^)(NSString *udid))completion {
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://domain.com/id.php"]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET"
path:#"http://domain.com/id.php"
parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *udid=[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"SERVER RESPONSE: %#",udid);
if (completion)
{
completion(udid);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// TODO: Handle error
}];
[operation start];
}
#end
Use it like this:
UDIDGen *getResponse = [[UDIDGen alloc] init];
[getResponse getUDIDWithCompletion:^(NSString *udid) {
NSLog(#"SERVER RESPONSE: %#",udid);
}];
You're starting the AFHTTPRequestOperation asynchronously. This means it's running in a separate non-blocking thread, and your method might return before the request has finished.
The easiest way would be to just do this, but this will most likely block the GUI while loading:
[operation startSynchronous];
The cleanest way would be to use a completion block or delegate yourself and inform the target class when a response has been received:
+ (void)getUDIDWithCompletion:(void (^)(NSString *udid))completion {
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://domain.com/id.php"]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET"
path:#"http://domain.com/id.php"
parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *udid=[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"SERVER RESPONSE: %#",udid);
if (completion)
{
completion(udid);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// TODO: Handle error
}];
[operation start];
}
You would call this method like so:
[YourCustomClass getUDIDWithCompletion:^(NSString *udid) {
// use udid / update GUI
}];
Related
I have a question about AFNetworking 2.0
I have to make a GET request like this:
http://myhost.come/entity?language=en&query=$UserId=14 and $EntityCreationDate>'2014-09-01'
How to generate the Dictionary parameters for this request?
In particular I not understand how can I build this: and $EntityCreationDate>'2014-09-01'
Here I found a good tutorial.
Which states a simple GET request can be sent like this:
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://samwize.com/"]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET"
path:#"http://samwize.com/api/pigs/"
parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// Print the response body in text
NSLog(#"Response: %#", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
I have an url request method, because each page will be used, I want to make it public.
(Poor standard of English, forgive me)
here is my class method:
- (void)httpRequest :(NSString *)url :(NSMutableArray *)array;
{
NSURL *myUrl = [NSURL URLWithString:url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:myUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"请求完成");
NSArray *arr;
arr = [NSJSerialization JSONObjectWithData:operation.responseData options:NSJSONReadingAllowFragments error:NULL];
[array addObjectsFromArray:arr];
???????
}failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"请求失败: %#", error);
??????
}];
[operation start];
}
I know how to pass simple parameters,like array and url.But when I use this method,code in the question mark will be different everytime.What can I do to make it can be passed,like parameters.
Like: [myApple httpRequest :myurl :myarray : (susucess .....) : (failure......)];
the question mark can be filled in: mytableView reloadData or nslog...
I'm not sure if i explained my question clearly,i don't know if block can solve this,thanks,waiting for your help.
Firstly, note that all method parameters in Objective-C should be named. So instead of - (void)httpRequest :(NSString *)url :(NSMutableArray *)array;, you should have something like:
- (void)performHttpRequestWithURL:(NSString *)url resultsArray:(NSMutableArray *)array;
To answer your question, you could pass your own completion block into your method. You can then call this block once AFNetworking has completed its HTTP request.
- (void)performHTTPRequestWithURL:(NSString *)urlString completion:(void (^)(NSArray *results, NSError *error))completion
{
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"请求完成");
NSArray *results = [NSJSerialization JSONObjectWithData:operation.responseData options:NSJSONReadingAllowFragments error:nil];
if (completion)
completion(results, nil);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"请求失败: %#", error);
if (completion)
completion(nil, error);
}];
[operation start];
}
You could then call your method and pass in a block:
[self performHTTPRequestWithURL:#"http://www.example.com" completion:^(NSArray *results, NSError *error) {
if (error)
{
// handle error
}
else
{
// reload tableview or similar
}
}];
Take a look at Apple's Blocks Programming Guide for more information on blocks.
I'm following the given example code
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:#"http://example.com/resources.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
To change the timeout and cache policy I 'hacked' the library and created
- (AFHTTPRequestOperation *)GET:(NSString *)URLString
parameters:(NSDictionary *)parameters
timeoutInterval:(NSTimeInterval)timeoutInterval
cachePolicy:(NSURLRequestCachePolicy)cachePolicy
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:#"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters];
[request setTimeoutInterval:timeoutInterval];
[request setCachePolicy:cachePolicy];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
Is there a clean way of doing this?
I'm a bit lazy to categorize or subclass. You can access the manager's request serializer directly:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer.timeoutInterval = INTERNET_TIMEOUT;
manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;
The best is to create a subclass
(you can also the same way add cache policy)
TimeoutAFHTTPRequestSerializer.h
#import "AFURLRequestSerialization.h"
#interface TimeoutAFHTTPRequestSerializer : AFHTTPRequestSerializer
#property (nonatomic, assign) NSTimeInterval timeout;
- (id)initWithTimeout:(NSTimeInterval)timeout;
#end
TimeoutAFHTTPRequestSerializer.m
#import "TimeoutAFHTTPRequestSerializer.h"
#implementation TimeoutAFHTTPRequestSerializer
- (id)initWithTimeout:(NSTimeInterval)timeout {
self = [super init];
if (self) {
self.timeout = timeout;
}
return self;
}
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(NSDictionary *)parameters
error:(NSError *__autoreleasing *)error
{
NSMutableURLRequest *request = [super requestWithMethod:method URLString:URLString parameters:parameters error:error];
if (self.timeout > 0) {
[request setTimeoutInterval:self.timeout];
}
return request;
}
#end
Use
self.requestOperationManager.requestSerializer = [[TimeoutAFHTTPRequestSerializer alloc] initWithTimeout:30];
You can also create a category AFHTTPRequestOperationManager+timeout to add this method without having to subclass AFHTTPRequestOperationManager.
Take a look at Method 1 for a cleaner way to do it: https://stackoverflow.com/a/21238330/435040
The difference is that I'm using subclassing and I'm not patching AFNetworking's code.
One thing that I forgot to mention. In that answer I'm only changing the timeout interval, but adding some other caching policy is just 1 more line of code.
Try something like :
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:kRequestTimout];
where kRequestTimout is the timeout duration you want
Then build your serialized request :
NSURLRequest *serializedRequest = [self.requestOperationManager.requestSerializer requestBySerializingRequest:request withParameters:parameters error:&error];
And create & add your request operation :
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:serializedRequest];
[operation setCompletionBlockWithSuccess:successBlock failure:failureBlock];
[self.requestOperationManager.operationQueue addOperation:operation];
I'm beginner in ios. This is a simple code example with AFNetworking.
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://yourServerAddress.com/"]];
[httpClient setParameterEncoding:AFFormURLParameterEncoding];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET"
path:#"http://yourServerAddress.com/example?name=foo"
parameters:nil];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
AFHTTPRequestOperation *operation = [httpClient HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Response: %#", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//-- This block gets invoked when the operation fails
NSLog(#"Error: %#", error.localizedDescription);
}];
The responseObject would be printed to the console, but my question is : How to get responseObject and use it in an another class or methods?
EDIT 1:
I'm tried to use a method :
- (void)responseFromBlock:(id)response {
NSLog(#"responseObject from the block : %#",response);
}
And i'm use it inside the block :
[self responseFromBlock:responseObject];
But the answer is:
response object from the block : <7b226572 726f7222 3a7b2263 6f646522 3a313230 307d2c22 72657375 6c74223a 5b7b2263 6964223a 22313234 222c2263 6e616d65 223a2244 69766174 227d2c7b 22636964...
It might look like this:
#interface YourClassDealingWithAFNetworking
#property (nonatomic, retain) id object;
#end
And then inside the block:
self.object = responseObject;
Now you've got it and can use it wherever you need
just don't forget to release it in dealloc:
self.object = nil;
I am using AFNetworking and I've read that synchronous responses are discouraged. yet I need to check whether a user already exist in the online database before the person can go to the next stage of the app. Yes, a typical registration process.
My code as it stands it returns NO because is asynchronous. I need to find a way to check for the success call and return YES or NO depending on this callback.
Could anyone point me in the right direction of how to write an app that waits for the success call so that I know that the user has not been set?
-(BOOL)doesTheUserExistAlreadyOnServer:(NSString *)parsedEmail
{
BOOL *methodResponse = NO;
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://www.myurl.co.uk/"]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST"
path:#"http://www.myurl.co.uk/igym.php"
parameters:#{#"myvar2":#"piggy"}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// Print the response body in text
// NSLog(#"Response: %#", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
if ([[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] isEqualToString:#"piggy"]) {
__block methodResponse = YES;
NSLog(#"%#",[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
return (BOOL)methodResponse;
}
EDIT:
I solved the problem using the following logic.
The user clicks on the registration button. The main method does all the preliminary non-web checks, then calls the [self doesTheUserExistAlreadyOnServer:_email.text];
that method code is now
-(void)doesTheUserExistAlreadyOnServer:(NSString *)parsedEmail
{
if(![_spinner isAnimating])
{
[_spinner startAnimating];
}
__block RegistrationViewController* me = self;
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://www.myurl.co.uk/"]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST"
path:#"http://www.myurl.co.uk/igym.php"
parameters:#{#"myvar2":#"piggy"}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// Print the response body in text
if ([[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] isEqualToString:#"piggy"]) {
NSLog(#"%#",[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
[me registrationPartTwo:YES];
} else
{
[me registrationPartTwo:NO];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
}
Then once that block/callback is successful it calls
-(void)registrationPartTwo:(BOOL)doesItExistOnServer
{
[_spinner stopAnimating];
NSString *emailAlreadyInUseMessage = [NSString stringWithFormat:#"This email is already in use"];
if (doesItExistOnServer)
{
self.screenMsg.text = emailAlreadyInUseMessage;
//here more code to send the user the the next step
}
}
basically i solved this using a 2method registration process dependant upon callback, dont know if thats the best or most efficient way. but thats the way i could solve it on my own.
Use a delegate callback to notify when the operation completes. If needed, before starting the operation, put up an UIActivityIndicatorView (the spinner) to prevent the user from interacting with the app.