We're converting our CURL HTTP Get requests to native IOS code. With CURL we can set two different timeouts - CURLOPT_CONNECTTIMEOUT - how long before a call fails if it cant connect, and CURLOPT_TIMEOUT - how long before a call fails if all the data hasnt been retrieved. If the connect fails we want it to return pretty quickly (10 seconds), but we download large chunks of data possibly on slow connections so we need the completion timeout to be quite large (5 minutes).
How do we set different timeouts using NSMutableURLRequest
Currently we are setting the single timeout like this
[urlRequest setTimeoutInterval:30.0f]
Is there a way to set two separate timeouts, like CURL does ? And which timeout are we currently setting ? The connection timeout or the completion one.
Thanks
shaun
That's a really good question. The documentation on it was unclear to me:
If during a connection attempt the request remains idle for longer than the timeout interval, the request is considered to have timed out. The default timeout interval is 60 seconds.
I did find this really helpful post in the Apple Developer Forums, which an Apple employee explains:
The timeoutInterval property is equivalent to
timeoutIntervalForRequest property.
He's referencing the property on NSURLSessionConfiguration, which can be attached to an NSURLSession. If you set the timeoutInterval of an NSURLRequest, it is used as the value for timeoutIntervalForRequest on the configuration. This property's documentation does provide some insight:
The request timeout interval controls how long (in seconds) a task
should wait for additional data to arrive before giving up. The timer
associated with this value is reset whenever new data arrives. When
the request timer reaches the specified interval without receiving any
new data, it triggers a timeout.
The default value is 60.
Based on that, it seems this value is actually neither!
Related
We set timeout interval for a request by NSMutableURLRequest timeoutInterval. As Apple's document described, it specifies the limit between packets, not the whole request. When we analyse our requests logs, some timeout request exceeded the seconds we set to timeoutInterval. We need timeout the requests accurately.
By reading document and blogs, the timeoutIntervalForRequest property in NSURLSessionConfiguration is the same as timeoutInterval. But the timeoutIntervalForResource property seems fit our requirement.
However, Mattt says in objc.io that timeoutIntervalForResource "should only really be used for background transfers". Can it be used in normal request? Such as query user info. Is it appropriate in this situation?
Thanks very much.
It can be used, but it rarely makes sense to do so.
The expected user experience from an iOS app is that when the user asks to download or view some web-based resource, the fetch should continue, retrying/resuming as needed, until the user explicitly cancels it.
That said, if you're talking about fetching something that isn't requested by the user, or if you are fetching additional optional data that you can live without, adding a resource timeout is probably fine. I'm not sure why you would bother to cancel it, though. After all, if you've already spent the network bandwidth to download half of the data, it probably makes sense to let it finish, or else that time is wasted.
Instead, it is usually better to time out any UI that is blocked by the fetch, if applicable, but continue fetching the request and cache it. That way, the next time the user does something that would cause a similar fetch to occur, you already have the data.
The only exception I can think of would be fetching video fragments or something similar, where if it takes too long, you need to abort the transfer and switch to a different, lower-quality stream. But in most apps, that should be handled by the HLS support in iOS itself, so you don't have to manage that.
I followed this tutorial Downloading files in background with URLSessionDownloadTask
And this Apple doc Downloading Files in the Background
I tried to setup because we have a long running API
let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).background")
config.timeoutIntervalForRequest = 120
config.timeoutIntervalForResource = 180
But I bumped into this weird issue:
If the server does not response with any piece of data, meaning that
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)
does not get called at all.
The app will kill off previous request and retry a new request every 66 seconds. I don't know where this number come from but from my experiment, it's roughly 66 seconds
If I set the timeoutIntervalForRequest = 10 the sure enough, the app will retry the request every 10 seconds but any attempt to set it above 66 seconds doesn't work
Not sure if anyone encounter the same issue and found a solution.
Just 1 note: the whole thing will timeout when it reach 180 seconds and the app stop retrying new requests
First, that's a bug. Please file it at bugreport.apple.com. The timeout should not be ignored like that. Of course, there's a decent chance that this is a power management issue and won't be fixed, so I wouldn't hold your breath.
Second, you're approaching the problem in a way that is pretty much guaranteed to cause problems even if the timeout bug were fixed. The fact that your server isn't sending back any bytes to keep the connection alive is, of course, the reason the iOS device is disconnecting, but even if you change that and make it send out a bogus header one byte at a time every five seconds until the data is ready, you'll still have problems.
Basically, on a mobile device, you really shouldn't keep a long-running connection open to a remote server for any reason. It massively wastes battery to keep the Wi-Fi radio on continuously, much less the cellular radio, and worse, that connection could fail at any time when the user walks out of range, switches cell sites, or otherwise momentarily loses connectivity. Networks are crap — cellular networks doubly so.
A much better approach for long-running server processing is to do it asynchronously:
Make the request to the server.
Have the server send you back a unique identifier associated with the request, and, optionally, an estimated completion time.
Wait until the estimated completion time, then ask the server how things are going (providing that unique identifier).
Continue to periodically poll the server until the server says that the task is completed (or has failed).
When the server says that the task is completed, issue a request to retrieve the results, then issue a request to free the completed results.
Periodically clean up old, uncollected results on the server with a cron job or similar.
This approach avoids keeping the radio hot except for a couple of seconds on either side of your polling requests, and it makes the timeout issue entirely moot.
This is an issue that's making me question my own sanity, but I'm posting the question in case it's something real rather than a problem of my own making.
I have an iOS app that is making use of the NSURLConnection class to send a request to a webserver. The object is instantiated and instructed to call back the delegate, which receives the corresponding notifications didReceiveResponse / didReceiveData / didFinishLoading / didFailWithError. Effectively the same code that is posted on Apple's dev page for using the class. The requests are all short POST transmissions with JSON data; the responses are also JSON-formatted, and come back from an Apache Tomcat Java Servlet.
For the most part it all works as advertised. The app sends a series of requests to the server in order to start a job and poll for partial results. Most of the exhanges are short, but sometimes the results can be up to about 100-200Kb maximum when there are partial results available.
The individual pieces of data get handed back by the operating system in chunks of about 10Kb each time, give or take. The transport is essentially instantaneous, as it is talking to a test server on the LAN.
However: after a few dozen polling operations, the rate of transport grinds to a near standstill. The sequence of response/data.../finished works normally: the webserver has delivered its payload, but the iOS app is receiving exactly 2896 bytes, with a periodicity of 20-30 seconds in between chunks. It is the correct data, and waiting about 5 minutes for 130Kb of data does confirm that it's operating correctly.
Nothing I do seems to conveniently work around it. I tried switching to the "async" invocation method with a response block; same result. Talking to a remote website rather than my LAN test deployment gets the same result. Running in simulator or iPhone gets the same result. The server returns content-length and doesn't try to do anything weird like keeping the connection alive.
Changing the frequency of the polling achieves little, unless I crank up the delay in between polling to 50 seconds, then everything works fine, presumably because it only ends up polling once or twice.
A hypothesis that fits this observation is that the NSURLConnection object hangs around long after it has been released, and chews up resources. Once a certain limit is hit, the progress rate grinds to a near halt. If the slowed down connection actually completes, subsequent connections work normally again, presumably because they've been cleaned up.
So does this sound familiar to anyone?
There is a read request timeout that can be specified in cassandra.yaml:
# How long the coordinator should wait for read operations to complete
read_request_timeout_in_ms: 10000
I need some clarifications regarding this timeout.
According to the client request documentation, the read request can be of two types: External request or Background repair request.
Q1: Is this timeout imposed on both type of requests and what happens in each case?
Now, focusing on just the external reads. Again, in the documentation linked above, it says that during the read, a background process is kicked off to maintain consistency.
Q2: For an external read request, does the timeout include the time taken for the background process?
I am asking these question because I want to impose a timeout on each read request, but I don't want it to affect any other background process linked to reads.
Q1: I believe the background read repair will use the read_request_timeout_in_ms for it’s operation.
Q2: For external requests from clients, the time taken for background repair is usually in the “background” and does not count towards the timeout value. [1] “read_request_timeout_in_ms” timing is started when the coordinator gets a request. For consistency less than ALL, if responses are received from the required number of replicas before the timeout value, the response is returned to the client. For consistency level ALL the response is not returned until the background read repair completes [2].
[1] http://www.datastax.com/documentation/cassandra/1.2/webhelp/?pagename=docs&version=1.2&file=#cassandra/architecture/../dml/dml_config_consistency_c.html
[2] http://wiki.apache.org/cassandra/ReadRepair
I have an app with dynamic data and the update method uses arrayWithContentsofURL and dictionaryWithContentsofURL to get the plists from a server in order to update my database.
My problem:
When there is no or not correctly working internet connection on the device this request simply tries to get the data for about a minute before it stops trying and continues execution.
Is there a way to maybe set a timeout for this function?
PS: I know this is probably the worst way to do this and I would be happy if someone could point me in the right direction :) I'm quite new to iOS programming so please be patient.
In my opinion it's best to use an NSMutableURLRequest with it.
Which has a - (void)setTimeoutInterval method. From the documentation:
The timeout interval, in seconds. If during a connection attempt the
request remains idle for longer than the timeout interval, the request
is considered to have timed out. The default timeout interval is 60
seconds.
Suggest you use an NSURLRequest to send the Request object. Its delegate functions will return you the plist.
You could take this example, about half way on that page it downloads a json object very much the same way as you could fetch a plist.