Cannot download image using AFHTTPRequestOperationManager - ios

I'm trying to download an image (jpeg) from a given URL using this code:
let manager = AFHTTPRequestOperationManager()
manager.responseSerializer = AFImageResponseSerializer()
manager.responseSerializer.acceptableContentTypes = NSSet(array: ["application/octet-stream"]) as Set<NSObject>
manager.GET(imageURL, parameters: nil,
success: { (operation: AFHTTPRequestOperation!, responseObject: AnyObject!) -> Void in
println("IMAGE SUCCESS")
}, failure: { (operation: AFHTTPRequestOperation!, error: NSError!) in
println("IMAGE FAIL")
})
With my internet at the moment, the success block is being called when testing on the simulator. However, the failure block is being called when testing on an iPhone. On another network, the success block is always called on both iPhone and simulator.
I can't figure out what's causing the issue. I have tried setting the Content-Type to "image/jpeg" but still got the same results. Did I miss something?
The error is:
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo=0x1675f381 {NSErrorFailingURLStringKey=<...>, NSErrorFailingURLKey=<...>, NSLocalizedDescription=The request timed out., NSUnderlyingError=0x166c7891 "The request timed out."}

Are you requesting many images (or doing many network requests)? If so, you might want to constrain the AFHTTPRequestOperationManager:
manager.operationQueue.maxConcurrentOperationCount = 5
If you don't do this and then proceed to initiate many requests, the "timeout" starts when the network requests are enqueued even though NSURLConnection (which AFHTTPRequestOperationManager uses behind the scenes) can only run 4 or 5 at a time and the latter requests may not even start until earlier ones finish. You want to start the timeout logic for network requests on the basis of when the request starts, not when the request was enqueued. By constraining the maxConcurrentOperationCount, you're ensuring that network requests aren't initiated before they can reasonably be performed.
Note, that presumes that you use the same AFHTTPRequestOperationManager for all of your requests, not instantiating new operation managers for new requests. But this will ensure that requests aren't started until the backlog of other requests has been reduced to an acceptable level.
--
As an aside, rather than replacing acceptableContentTypes, I'd merely suggest augmenting it:
var acceptableContentTypes = manager.responseSerializer.acceptableContentTypes ?? Set<NSObject>()
acceptableContentTypes.insert("application/octet-stream")
manager.responseSerializer.acceptableContentTypes = acceptableContentTypes
Frankly, I'd rather see you fix the Content-Type of the images you're retrieving from the server so that it didn't send application/octet-stream responses, but if you can't do that, the above is probably a more prudent way to adjust the content types. This ensures that you accept both your custom application/octet-stream type, as well as the standard image types.

Related

API request times out ONLY if I pass parameters

I am trying to figure out why any time I pass valid json to my server for a GET request the connection times out. I do not have any problems passing json to my server in any other request type other than GET... I've tested the server-side code locally and the queries work as expected.
I want to fetch a specific user from my database and I need to pass in a username, so I send the username to the server.
The error I keep getting (If I don't send any paramaters to the server, and just return current_user, it works and I don't get this error)
Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSUnderlyingError=0x604000454c40 {Error Domain=kCFErrorDomainCFNetwork Code=-1001
Here is the part of the API call code where I set the request type and values to send to the server, in case:
let request = NSMutableURLRequest(url: url as URL)
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
request.httpMethod = "GET"
Server set up, just in case it matters:
Digitalocean droplet, Linux, Ubuntu 16.04, Nginx
EDIT/ UPDATE
If I change the method from a GET to a POST (and keep the server code exactly the same), the server sends the correct data back immediately.
The server side code for this is very short, so I really don't see how it can be timing out due to optimization:
user = UserModel.find_by_username(data['username'])
if user:
return {"response": [user.json()]}, 200
return {"response": "user not found"}, 404
It really seems as if we can't send json via a GET method. It doesn't seem to work on both a simulator nor an actual device...I saw a similar SO post where someone commented exactly what I'm experiencing. Changing GET to POST was the fix....but it is a GET request, so why wouldn't this work?
GET-Method does not support http body. When you send your parameter as url encoded it will work.
Example:
http://www.example.de?username=abc

Network errors in Alamofire

I am a newbie to iOS and using Alamofire in my app. Everything is working fine. Now I want to implement network errors. I have searched about it and here are my findings:
We can implment request timed out in the following way:
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 10
let urlVerifyEmail = ####
manager.request(urlVerifyEmail, method: .post, parameters: ["user_email" : email], encoding: JSONEncoding.default, headers: nil).responseJSON(completionHandler: { response in
switch response.result {
case .success:
print (“success”)
case .failure(let serverError):
if (serverError._code == NSURLErrorTimedOut)
{
print(”Request timed out”)
}
else
{
print(”Error sending request to server”)
}
}
}
)
I have read there official docs too. However I am not having much clarity.
What does request timed out actually mean? Does it mean that the app is taking too long to send the request? or server is taking too long to send the response back?
What are other types or network errors in Alamofire? What if the connection wasn't successfully made? What if it broke while the request was getting sent? What if it broke while the response was coming back?
If error code for request timed out is -1001 then what are the codes for other network errors? In order to provide the users with the best experience, which is the best approach to cover all of network errors?

How to know if NSURLSessionDataTask response came from cache?

I would like to determine if the response from NSURLSessionDataTask came from cache, or was served from server
I'am creating my NSURLSessionDataTask from
request.cachePolicy = NSURLRequestUseProtocolCachePolicy;
Two easy options come to mind:
Call [[NSURLCache sharedURLCache] cachedResponseForRequest:request] before you make the request, store the cached response, then do that again after you finish receiving data, and compare the two cached responses to see if they are the same.
Make an initial request with the NSURLRequestReturnCacheDataDontLoad cache policy, and if that fails, make a second request with a more sane policy.
The first approach is usually preferable, as the second approach will return data that exists in the cache even if it is stale. However, in some rare cases (e.g. an offline mode), that might be what you want, which is why I mentioned it.
In order to know whether an URLSessionDataTask response comes from the cache or the network, you have to use a custom URLSession and provide it with an URLSessionTaskDelegate which has to implement the following method:
func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics)
You'll find very useful information in metrics, especially the list of transactions metrics for the request. Each transaction metrics has a property resourceFetchType which can either be .localCache, .networklLoad, .serverPush or .unknown.
More information here: Apple documentation regarding URLSessionTaskDelegate
If your information need is only out of curiosity you can view your network usage the Xcode runtime network metics. Change the policy to a different setting and observe the difference.
If you want to use caching then it's not a good answer to disable caching on the client.
if the server has set cache headers (etag, cache-control="no-store") then NSURLSession will revalidate and serve you cached / fresh response based on the 200 / 304 response from the server. However in your code you will always see statusCode 200 regardless of if NSUrlSession received 200 or 304. This is limiting, because you may want to skip parsing, re-creating objects etc when the response hasnt changed.
THe workaround I did, is to use the etag value in the response to determine if something has changed
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse *)response;
NSString *etag = (httpResp && [httpResp isKindOfClass:[NSHTTPURLResponse class]]) ? httpResp.allHeaderFields[#"Etag"] : nil;
BOOL somethingHasChanged = [etag isEqualToString:oldEtag];

Alamofire JSON POST result in Error: The network connection was lost

Several people get this error for different reasons. None of the answers I've found have solved my problem.
I use Timberjack to log my Alamofire requests.
All my GET requests work fine and I receive data in JSON.
My POSTs on the other hand works around 1 out 10 times every time if the POST includes a JSON body.
The server does not specify any Keep-Alive header.
Deployment target is iOS 9.0
This is my shared Manager with Timberjack:
class HTTPManager: Alamofire.Manager{
static let sharedManager: HTTPManager = {
let configuration = Timberjack.defaultSessionConfiguration()
let manager = HTTPManager(configuration: configuration)
return manager
}()
}
Defining the request:
let parameters: [String: AnyObject] = ["status":status]
request = HTTPManager.sharedManager.request(.POST, "\(baseURL)\(uri)", parameters: parameters, encoding: .JSON).validate()
Sending the request:
request!.responseJSON(queue: queue, options: .AllowFragments, completionHandler: { (response) in
//Handling the response
})
Most of the time the server receives an empty JSON body. But sometimes it does work and the body is received and the server responds with an OK. When it doesn't work I receive the error:
Error: The network connection was lost.
FAILURE: Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost."
UserInfo={NSUnderlyingError=0x12fa47cb0 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "The network connection was lost."
If someone can explain what's happening here I would be forever grateful :)
EDIT 1
We let the server respond every call with "Connection": "close" which did nothing to help the problem. The app always sends "Connection": "keep-alive" by default and it cannot be changed. Could this be the problem? That the app thinks the connection is open even though it's closed by the server? But even though you wait a few minutes it seems as though the POST call only works at random.
EDIT 2
Even when I wait 30 seconds between GET(s) and/or POST(s). GET always works. POST works at random (rarely). I get the -1005 error on most POSTs. Even though I get the network connection was lost error the server still receive my request but the JSON body is missing.
EDIT 3 - Ugly solution
In my response I check for the error code -1005, when I receive this error I just recreate the request and try again. This results in sending around 2-4 POST requests to the server where one POST works and the rest have empty JSON bodies.
Restart simulator or kill your app by throwing out from tasks.
Or check more solutions to this error code:
NSURLErrorDomain Code -1005 The network connection was lost

firefox addon-sdk : handle http request timeout

I'm building a firefox add-on using the add-on sdk. I need to make a http request to a certain page and I want to handle the connection timeout but couldn't find anything in the api: https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/request.html
What I'm actually looking is a callback in case the client couldn't connect to the server.
Is there a way to achieve this?
The SDK request will always call onComplete, when the request is considered done for the network. This means that onComplete is called in any case, disregarding if the request returned an error or a success.
In order to detect which error you've got, you need to check the Response object's (the object passed to the onComplete function) property "status" (response.status). It holds the status code for the request. To look up status codes, consider the list on the mozilla developer network. If the response status is 0, the request has failed completely and the user is probably offline, or the target couldn't be reached.
A timeout would either be a status code 504 or 0. The implementation would be similar to this:
var Request = require("sdk/request");
Request({
url: "http://foo.bar/request.target",
onComplete: function(response) {
if(response.status==0||response.status==504) {
// do connection timeout handling
}
// probably check for other status codes
else {
// assume the request went well
}
}
}).get();
I personally use a validation function on the request object, which returns me a number which depends whether I've got a correct response, an error from the web server or a connection issue (4xx and 0 status codes).

Resources