Server Side Events with AFNetworking 3.0 - ios

I want to listen for server side events and update accordingly using AFNetworking 3.0. I found info here nshipster.com/afnetworking-2
This is for AFNetworking 2.0.
This is what the code looks like for the 2.0 version,
NSURL *URL = [NSURL URLWithString:#"http://example.com"];
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:URL];
[manager GET:#"/resources" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
[resources addObjectsFromArray:responseObject[#"resources"]];
[manager SUBSCRIBE:#"/resources" usingBlock:^(NSArray *operations, NSError *error) {
for (AFJSONPatchOperation *operation in operations) {
switch (operation.type) {
case AFJSONAddOperationType:
[resources addObject:operation.value];
break;
default:
break;
}
}
} error:nil];
} failure:nil];
The code above seems to be what I am looking for but with using AFN 3.0.
Anyone have any ideas?

If I recall correctly (that was a few years ago) support for Server-Sent Events was first moved from AFNetworking core to a separate library called AFRocketClient, and then was eventually abandoned.
Two possible solutions:
Although the original AFRocketClient source repo has been wiped, there is a mirror here which still contains the source. You could try to make that work with AFNetworking 3.
Alternatively, you could have a look at EventSource, an implementation of SSE in Swift.
I haven't seen too much usage of SSE in iOS apps. I think the Apple push notification service is a more popular approach for server-to-client notifications.

Related

Why does AFNetworking hangs on first request?

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.

After setting custom headers in AFNetworking 2.x, the parameters are not being received by the RESTful API

I followed the suggested solution at AFNetworking 2.0 add headers to GET request to specify custom headers for the request with the following code snippet:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setValue:someID forHTTPHeaderField:#"some_id"];
NSDictionary *parameters = #{#"id": user.id, #"birthday": user.birthday};
[manager POST:[NSString stringWithFormat:#"%#/user_create",BaseURLString] parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
if (responseObject[#"error"])
{
NSLog(#"REST User Create Response Error: %#", responseObject[#"error"]);
}
else
{
[self saveUserDetails:responseObject];
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"REST User Create Error: %#", error);
}];
But what happens when this gets executed is I get an error in the response from the API stating all my parameters are missing. This same block of code used to work before (without setting the custom header and when the API didn't require them originally).
Does anybody know how to properly set both custom headers and POST parameters?
Thanks,
Nino
Your code looks fine to me.
A few things to try:
Try using AFNetworkActivityLogger to see what's actually being sent (set it to AFLoggerLevelDebug.) You can also use a web proxy like Charles or a protocol analyzer like Wireshark.
If you determine the data is not being sent properly, set a breakpoint in [AFURLRequestSerialization -requestBySerializingRequest:withParameters:error:]. This is where your HTTP headers and parameters are added to the URL request. The method is pretty straightforward; you should be able to step through and watch as stuff is added to the request and determine why it gets skipped.
NOTE: AFURLRequestSerialization.m contains multiple subclasses of AFURLRequestSerialization. Set a breakpoint in the super implementation, as well as in the AFJSONRequestSerializer implementation.
Examples that could cause this behavior:
parameters is nil.
you've added POST to HTTPMethodsEncodingParametersInURI but
your API is not prepared to handle parameters appended to a URL on a POST request, or
the queryStringSerialization block is nil AND queryStringSerializationStyle is set to something other than AFHTTPRequestQueryStringDefaultStyle.
NSJSONSerialization can't handle your parameters dictionary
One side note (unrelated to your problem), if you use AFHTTPRequestOperationManager's initWithBaseURL: method, and keep a strong reference to your manager, you won't have to do that [NSString -stringWithFormat:] stuff to construct your URL.
I discovered that I should have set my request serializer to the following instead since my API doesn't require JSON-formatted parameters.
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
Hope this helps others dealing with the same situation as mine.

Google Places API Autocomplete on iOS

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

avoid duplicate HTTP requests with AFNetworking

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;
}

To understand the AFNetworking ios library AFXMLRequestOperation

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

Resources