Can this image download technique use timeouts: Why doesn't image load on main thread??
Or must I use NSURLSession instead if I want to use timeouts?
You are looking for the timeoutintervalForResource property. If you use URLSession.shared, the default timeout is 7 days. If you want to use a different timeout, you need to create your own session:
let config = URLSessionConfiguration.default
config.timeoutIntervalForResource = 60 // timeout, in seconds
// A 20 MB image from NASA
let url = URL(string: "https://www.nasa.gov/sites/default/files/thumbnails/image/hs-2015-02-a-hires_jpg.jpg")!
let session = URLSession(configuration: config)
session.dataTask(with: url) { data, response, error in
if let error = error {
print(error)
}
// do something
}.resume()
Lower the timeout enough and you will see a timeout error. Note that URLSessionConfiguration has 2 timeouts: timeoutIntervalForResource and timeoutIntervalForRequest:
...Resource is the time to wait for whole network operation to finish (default is 7 days)
...Request is the time to wait for next chunk of data to arrive (default is 60 seconds)
If your goal is to download something in x minutes, using ...Resource. If your goal is "network must response within x seconds or it's down", use ...Request.
No, you don't have to use NSURLSession. The timeout properties are in URLSesssionConfiguration and you just need to create an instance of URLSession using your desired configuration.
So, rather than using URLSession.shared directly you would need to make your own instance of URLSession and start the dataTask from that instance.
You are probably interested in timeoutIntervalForResource, which I think defaults to 7 days.
Here is a relevant Swift fragment from the answer to this question:
let sessionConfig = URLSessionConfiguration.default
sessionConfig.timeoutIntervalForRequest = 30.0
sessionConfig.timeoutIntervalForResource = 60.0
let session = URLSession(configuration: sessionConfig)
Related
This question already has answers here:
Using NSURLSession from a Swift command line program
(3 answers)
Closed 2 years ago.
I am trying to send HTTP request with POST method in a command line app. Using JSON as body of the request. I am using session.uploadTask to send this request and use JSON data serialised from simple Dictionary. Maybe I missed something but it doesn't work. I even tried to write my request to console and it looks good -> it is the same format as iTranslate API wants.
//creating new session
let session = URLSession.shared
let url = URL(string: "https://api.itranslate.com/translate/v1")!
//setting httpMethod to POST
var request = URLRequest(url: url)
request.httpMethod = "POST"
//setting header
request.setValue("application/json", forHTTPHeaderField: "content-type")
//dictionary with json
let json = ["key": "...", "source": ["dialect":"en", "text": "How are you?"], "target": ["dialect": "es"]] as [String : Any]
//serialization from json to jsonData
let jsonData = try! JSONSerialization.data(withJSONObject: json, options: [])
let task = session.uploadTask(with: request, from: jsonData) { data, response, error in
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print(dataString)
}
}
task.resume()
In most apps, there is a “run loop” that is constantly running, responding to user interaction, keeping the app alive until the user explicitly terminates it. In that scenario, we can initiate the asynchronous network request, confident that the run loop is keeping our app alive, and when the network response comes in, our completion handler closure is called.
In a command line app, on the other hand, when it gets to the end of the code, the app simply terminates, never giving the asynchronous network request a chance to respond, and the closure will never be called.
You can, however, have the command line app wait for the request to finish before terminating, e.g.
let semaphore = DispatchSemaphore(value: 0)
let task = session.uploadTask(with: request, from: jsonData) { data, response, error in
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print(dataString)
}
semaphore.signal()
}
task.resume()
semaphore.wait()
Now, this technique (waiting on the main thread, thereby blocking that thread) is a very bad practice in standard apps, because it will block the main thread while the network request is running. In a typical app, blocking the main thread would freeze the UI, offering a substandard user experience (and the OS might even kill the app for not being responsive). We generally would never block the main thread.
But in a command line app, where you often don't worry about a graceful and responsive UI, blocking the main thread is less of an issue and the above can keep the app alive while the network request is running.
I am sending data to api using the post method. But while working in advance, I have now moved my server to the windows sdd server and started to get the problem I wrote below all the time. While working on Similator, it doesn't work when I try it on my physical phone, I get this problem. What is the problem? A situation related to the firewall? Because I started to get this problem after moving the server. Or another problem?
NSErrorFailingURLStringKey=....php, NSErrorFailingURLKey=....php,
_kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=The request timed out.}
#objc func veriGonder() {
let url = NSURL(string: "...php")
var request = URLRequest(url: url! as URL)
request.httpMethod = "POST"
...
dataString = dataString + "&formCLASSNAMESTARIH\(verıTURUSTarih)"
dataString = dataString + "&formCLASSNAMESZAMAN\(verıTURUsZaman)"
...
let dataD = dataString.data(using: .utf8)
do {
let uploadJob = URLSession.shared.uploadTask(with: request, from: dataD)
{
data, response, error in
...
}
Here as the error specifies your request timed out. So to fix this either you need to increase the timeout interval or increase the server response interval from server-side. So, if it's the first option here's the code you need:
request.timeoutInterval = 100 // increase this to your desired value.
I know how to make a regular API call using swift. What I am not able to understand is how to make the API call to be repeated until required.
I want to call the API every one second
API Call Code Snippet:
let url = URL(string: "https://api.darksky.net/forecast/34eaef38915078ea03c22bb9063bd7ea/37.8267,-122.4233")
let request = URLRequest(url: url!, cachePolicy: URLRequest.CachePolicy.reloadIgnoringCacheData, timeoutInterval: 10)
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: OperationQueue.main)
let task: URLSessionDataTask = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) in
if let error = error {
print(error)
} else if let data = data,
let dataDictionary = try! JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary {
print("API Data:")
print(dataDictionary)
}
})
task.resume()
Note: This is not the actual API I will be calling
Ideally, for software solutions like financials you mentioned, the server must have support for some sort of Long Polling / websockets mechanism where once connection is established server feeds the client with new values whenever there are updates (refer : https://stackoverflow.com/a/12855533/1436617)
If server does not support : (Not the ideal solution) :
You can actually use recursion in this. On response (both success & failure) of the request again call the same function. That way you can continuously keep polling.
Remember to keep request timer short (5 or 10 seconds instead of 60 seconds) so that if there happens to be an network issue you can quickly make the next call.
I am using the Alamofire Swift library
Alamofire.request
(RestApiManager.sharedInstance.baseURL+"login?language="+lang,
method: .post,
parameters: requestDictionary,
encoding: URLEncoding.httpBody,
headers: headers
).responseObject(keyPath: "") { (response: DataResponse<User>) in
let user = response.result.value
print(user?.status)
print(user?.message)
}
So simply, I want to put a timeout of 60 seconds on every call I make.And i like to give a message connection timeout after 60 seconds. I also want to know, if there exists an internet connection or not. If it doesnt exist, I like to avoid calling alamofire.
Here's the Swift 3.0 / Alamofire 4.0 code to get an alamofireManager that has a 60 second timeout.
You need create a global variable for the request manager:
var alamoFireManager = Alamofire.Manager.sharedInstance
And after configure the custom parameters:
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.timeoutIntervalForRequest = 60 // seconds
configuration.timeoutIntervalForResource = 60
self.alamoFireManager = Alamofire.Manager(configuration: configuration)
I'm trying to set a client-side timeout per request for Alamofire for Swift. The lead architect told me to set this on NSURLRequest, but I'm completely confused on how to actually do that in practice.
Can someone who has done this give an example? Thanks!
I think this code may works.
var alamofireManager : Alamofire.Manager?
func some(){
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.timeoutIntervalForResource = 2 // seconds
self.alamofireManager = Alamofire.Manager(configuration: configuration)
self.alamofireManager!.request(.GET, "http://example.com/")
.response { (request, response, data, error) in
}
}
This is how you can use a per-request timeout using URLRequest:
Alamofire.request(URLRequest(url: ..., cachePolicy: ..., timeoutInterval: 10))
.response(...)