Hi I am new to AFNetworking library and am integrating it for first time in my iPAD app. However I am having hard time finding a working example for reference.
I found the following solution by rckoenes here AFNetworking POST and get Data back. However I am hard time implementing it. Since that answer is already marked as correct. I am assuming the library as changed since that time and the example might not be relevant.
Any help with a working example reference is highly appreciated.
NSURLRequest *request = [wsu generateURLRequest:getFavAPI method : #"GET" arguments:nil eventName:USER_FAVS_SUCCESS_NOTIF contentType:nil];
NSLog(#"Request %#", request.description);
AFXMLRequestOperation *favOpertion = [[AFXMLRequestOperation alloc] initWithRequest:request];
//favOpertion.responseXMLParser = [NSXMLParser initialize]; // error: setting the readOnly property.
[favOpertion setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *favOpertion, id responseObject) {
NSLog(#"Response: %#",[favOpertion responseString]); // Never reached here
} failure:^(AFHTTPRequestOperation *favOpertion, NSError *error) {
NSLog(#"Error: %#",[favOpertion error]); // never reached here
}];
The code prints the request desc as valid: http://[server name]/favorites/folders/user/[uid]>
Here is an example of the default SAX style parsing. There are some other XML request operations people have made that give you an actual XML document:
AFKissXMLRequestOperation
AFGXMLRequestOperation
Related
I am completely baffled on this. Each time I test my app in the simulator or on a real device, it hangs for 30,40,60 seconds on this bit of code, but all following request to this API call will load in milliseconds.
I thought it was related to DNS resolving for the first time, so I switched to an IP address for testing and that did not resolve the issue.
If it's the first request after the app starts, it will just hang for a large amount of time, once it has loaded, you can open the view for the same data set or another and it load the list very fast.
Any recommendations?
-(void)getVendorImages {
//Alloc the image list
self.imageList = [[NSMutableArray alloc] init];
// Prepare the request
NSString* vendorImagesApi = [NSString stringWithFormat:#"%#%#",#"http://example.com/api/v1/vendor/images/",self.imageData.vendorId];
NSLog(#"Getting list of images %#",vendorImagesApi);
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:vendorImagesApi
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// NSLog(#"JSON: %#", responseObject);
//Get images
for (id imageData in responseObject)
{
// prepare image url
NSString* imageUrl = [NSString stringWithFormat:#"%#%#%#",#"http://example.com/images/",imageData[#"id"],#"-650x650.jpg"];
NSLog(#"Putting this in in a list: %#", imageUrl);
[self.imageList addObject:imageUrl];
}
[self.tableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
EDIT:
Here is the thread stack
So after a bunch of digging, I removed all the files for AFNetworking, then installed it all again, including the UIKit+AFNetworking folder, after that I removed all the frameworks and added back UIKit and SystemConfig. Lastly one of my views that loaded at the start of the app had it's own NSURLConnectionDelegate. I removed all that and had it use AFNetworking, and that did the trick. Apparently the first run that was stalling the connection for AFNetworking was because it was likely fighting over who could use the service.
I'm writing a small iOS client for a server protected with OAuth2.
I'm wondering if is it possible using AFOAuth2Manager [here] auto-refreshing the expired token.
The idea is that the logic for refreshing the client when the server responds with a 401, or raise an error when the refresh method returns a 401 should be quite common, so probably it is integrated in some library.
I created a subclass of AFOAuth2Manager
In this subclass I override this method:
- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {
return [self HTTPRequestOperationWithRequest:request
success:success
failure:failure
checkIfTokenIsExpired:YES];
}
calling a custom method with an additional parameter: checkIfTokenIsExpired. This is required in order to avoid infinite loops.
The implementation of this method is straigth forward: if we don't need to check the token just call the super class.
if (!checkIfTokenIsExpired) {
return [super HTTPRequestOperationWithRequest:request
success:success
failure:failure];
}
otherwise we perform the request with a custom failure block
else {
return [super HTTPRequestOperationWithRequest:request
success:success
failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
if (operation.response.statusCode == ERROR_CODE_UNAUTHORIZED) { //1
[self reauthorizeWithSuccess: ^{ //2
NSURLRequest *req = [self.requestSerializer requestByAddingHeadersToRequest:request]; //3
AFHTTPRequestOperation *moperation = [self HTTPRequestOperationWithRequest:req //4
success:success
failure:failure
checkIfTokenIsExpired:NO];
[self.operationQueue addOperation:moperation]; //5
} failure: ^(NSError *error) {
failure(nil, error);
}];
}
else {
failure(operation, error); //6
}
}];
}
//1: check the http status code, if 401 try to automatically re-authorize.
//2: reauthorize is a private mathod that uses AFOAuthManager to refresh the token.
//3: In this case we are re-authorized with success and we want to resubmit a copy of the previous request. The method requestByAddingHeadersToRequest: just copy all the header fields from the previous request.
//4: Create a copy of the previous request, but this time the last parameter is false because we don't want check again! The successBlock and failureBlock are the same of the previous request.
//5: Add the operation to the queue.
//6: If the reauthorize method fails just call the failure block.
Unfortunately I didn't found any framework for solve this problem so I wrote a short wrapper around AFNetworking (if someone is interested I can publish on github)
The logic is to execute the request, and in case of http response 401, try to refresh the auth-token and when it's done to re-execute the previous request.
I was searching an answer for this problem and "Matt", the creator of AFNetworking, suggest this:
the best solution I've found for dealing with this is to use dependent
NSOperations to check for a valid, un-expired token before any
outgoing request is allowed to go through. At that point, it's up to
the developer to determine the best course of action for refreshing
the token, or acquiring a new one in the first place.
Simple, but effective?, trying now, will edit with report...
Swift solution with Alamofire 4.0. Based on RequestAdapter and RequestRetrier protocols: example link
I am attempting to use AFNetworking to upload an audio file to Cloudinary through a POST call. I have been using AFNetworking to send POST calls to my own server with ease but this is the first time I have tried sending an NSData object through AFNetworking which is where I think the problem lies and am not exactly sure how I need to adjust the call to get it to work.
My Code
I need to send 4 parameters through the post call to the Cloudinary service which I place in to a dictionary....
NSData *audioFile = [[NSData alloc]initWithContentsOfURL:_audioRecorder.url];
NSMutableDictionary *myParam = [NSMutableDictionary dictionary];
[myParam setValue:#(nowTime) forKey:#"timestamp"];
[myParam setValue:audioFile forKey:#"file"];
[myParam setValue:api_key forKey:#"api_key"];
[myParam setValue:finalSignature forKey:#"signature"];
[cloudManager setUpDataCall:#"/upload" withParameters:myParam];
The setUpDataCall goes to a subclass of AFHTTPSessionManager with this code. This is the rest of the post URL if you were curious #"https://api.cloudinary.com/v1_1/karmo/raw"
- (void)setUpDataCall:(NSString *)callURL withParameters:(NSDictionary *)parameters {
[self POST:callURL parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
if ([self.delegate respondsToSelector:#selector(cloudManager:didReturnData:)]){
//Success :)
}
}failure:^(NSURLSessionDataTask *task, NSError *error) {
if ([self.delegate respondsToSelector:#selector(cloudManager:didFailWithError: )]) {
//Error :(
}
}];
}
The Error
I get this error when I try to send the file Invalid type in JSON write (NSConcreteData).
When I add an exception breakpoint it identifies this
[mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
Which is in this method in the AFURLRequestSerialization.m class.
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
I have seen a few other stackoverflow questions like the one below but I am not sure if those are still relevant as the code from those posts seem to throw errors in the code as I am guessing AFNetworking has been updated since those posts.
AFNetworking Uploading a file
Any code help or examples would be greatly appreciated. Thanks a bunch for taking the time.
Have you tried the suggestions as explained in the following post's comments?
Error with NSJSONSerialization - Invalid type in JSON write (Menu)
Over the last few days I've been struggling to get Google Places autocomplete to work on my app, and no matter what I do, I always get the REQUEST_DENIED error.
I followed Google's "tutorial" for the implementation, Places API is enabled, API key has the correct bundle ID, I also tested with a Browser ID, with no success.
I am pretty sure it has something to do with the key, though. Curiously, on the Android version of the app, the service will work with the Browser Key. And on the browser, obviously, it works with that key too.
These are the two keys I experimented with:
And this is my implementation code, using AFNetworking:
NSURL *url = [NSURL URLWithString:#"https://maps.googleapis.com/maps/api/place/autocomplete/"];
NSDictionary *params = #{#"input" : [input stringByReplacingOccurrencesOfString:#" " withString:#"+"],
#"location" : [NSString stringWithFormat:#"%f,%f", searchCoordinate.latitude, searchCoordinate.longitude],
#"sensor" : #(true),
// #"language" : #"pt_BR",
// #"types" : #"(regions)",
// #"components" : #"components=country:br",
#"key" : GOOGLE_API_KEY};
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[httpClient setParameterEncoding:AFFormURLParameterEncoding];
[httpClient getPath:#"json"
parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSDictionary *JSON = [[NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil] dictionaryWithoutNulls];
if (completion) {
completion(JSON[#"predictions"]);
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *errorResponse) {
NSLog(#"[HTTPClient Error]: %# for URL %#", [errorResponse localizedDescription], [[[operation request] URL] path]);
}];
I know there are some questions like this one here, but some are old, and say that Places API does not work on Android or iOS, which clearly is not the case anymore, since Google itself publishes examples on both platforms, as seen on Places API page: https://developers.google.com/places/documentation/autocomplete
A workaround I'm currently using is Apple's GeoCoding system, which works good when you type the full address, but is terrible with half-typed phrases. This is not good at all, I'd really like to use Google's API.
I got it!
The paramenter sensor should receive either #"true"or #"false", and not #(true)or #(false).
For the record, the API key used is indeed the Browser Key, and not an iOS key.
You should init httpClient with base URL #"https://maps.googleapis.com/" and get path /maps/api/place/autocomplete/json. Base URL - host only, it does not take other parts of path, so in your case you get request to URL "https://maps.googleapis.com/json".
https://maps.googleapis.com/maps/api/geocode/json?address="%#","%#"&sensor=false,yourAddress,your c
I would like to find out if it's possible to avoid duplicate HTTP requests with AFNetworking. Specifically, my app may generate multiple HTTP requests which all have the same url. I would like to prevent AFNetworking from processing duplicates of the same url.
Im not sure if this can be done in AFNetworking or the underlying iOS sdk. I understand that i could manually keep trac of pending url request and avoid duplicates that way, but was wondering if there is a lower level functionality already available to take care of this.
Thanks.
Your best bet is to subclass AFHTTPRequestOperationManager's HTTP request operations and keep track of them there if you want to track requests the same way for each request, otherwise the logic will need to be elsewhere.
AFNetworking doesn't support this because there is probably some logic relevant to when you should and when you should not execute a duplicate request, which would be highly customizable (not generic enough for the framework)
I made a category that checks for in-progress GET requests before making new ones.
https://github.com/NSElvis/AFHTTPSessionManager-AFUniqueGET
It does this by using the method getTasksWithCompletionHandler of the session.
I had the same problem. I have a chat-application and I need to show user avatar for each message. So I made few same requests and I've resolved this issue.
First, I add NSDictionary with NSString avatar URLs keys and completion blocks objects:
#property (strong, nonatomic) NSMutableDictionary* successBlocksDictForGetAvatar;
And here's my method to get user avatar image:
- (void)getAvatarForUser:(ETBUser*)user
completion:(void(^)())completionBlock
{
if (user.avatarURL)
{
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:user.avatarURL]];
if (self.successBlocksDictForGetAvatar[user.avatarURL])
[self.successBlocksDictForGetAvatar[user.avatarURL] addObject:completionBlock];
else
{
NSMutableSet* set = [[NSMutableSet alloc] initWithObjects:completionBlock, nil];
[self.successBlocksDictForGetAvatar setObject:set forKey:user.avatarURL];
AFHTTPRequestOperation* operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
UIImage* avatarImage = [UIImage imageWithData:responseObject];
if (avatarImage)
{
user.avatar = avatarImage;
[[DataManager shared] saveAvatar];
[((NSSet*)self.successBlocksDictForGetAvatar[user.avatarURL]) enumerateObjectsUsingBlock:^(void(^successBlock)(), BOOL *stop) {
successBlock();
}];
[self.successBlocksDictForGetAvatar removeObjectForKey:user.avatarURL];
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self.successBlocksDictForGetAvatar removeObjectForKey:user.avatarURL];
}];
[self.manager.operationQueue addOperation:operation];
}
}
}
Here I check if my dictionary contains request. If YES, I add completion block for user in dictionary. Otherwise I setObject:forKey: and make AFNetworking request. In success and fail blocks I clean my dictionary.
P.S. Here's my manager getter:
- (AFHTTPRequestOperationManager*)manager
{
if (!_manager)
{
_manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:kBaseURL];
[_manager.requestSerializer setValue:NetworkConstantsHeaderAcceptValue forHTTPHeaderField:NetworkConstantsHeaderAcceptKey];
[_manager.operationQueue setMaxConcurrentOperationCount:1];
}
return _manager;
}