How to show message when Task fails due to Timeout - swift

I need a clean way for this example to print("ERROR") when the task ends due to the timeout.
func getUserDataService() -> Bool{
var getDataStatus = false
//Create the url with NSURL
let url = URL(string: "http://someurl")! //change the url
//Create the session object
let session = URLSession.shared
//Now create the URLRequest object using the url object
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.timeoutInterval = 15
//create dataTask using the session object to send data to the server
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
if let httpResponse = response as? HTTPURLResponse {
if httpResponse.statusCode == 200 {
getDataStatus = true
} else {
}
}
}
} catch let error {
print(error)
}
})
task.resume()
return getDataStatus
}
It prints the following when it fails, but I'm not sure where to control this.
2019-05-27 13:53:58.501322-0400 AppName[60195:841789] Task <AB410EF3-5520-44AD-A458-DD75D1F6AD09>.<3> finished with error - code: -1001
2019-05-27 13:53:58.505525-0400 AppName[60195:842001] Task <AB410EF3-5520-44AD-A458-DD75D1F6AD09>.<3> HTTP load failed (error code: -999 [1:89])

Please read the documentation URL Loading System Error Codes. The timeout error is -1001 aka NSURLErrorTimedOut
The error is returned in the completion handler of the data task. Handle it!
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
if let nserror = error as NSError?,
nserror.code == NSURLErrorTimedOut {
// do something
return
}
Apart from that you cannot return a Bool value from that method, you have to add a completion handler
func getUserDataService(completion: #escaping (Bool) -> Void) {
...
And call
completion(true)
and delete
return getDataStatus

Related

Passing JSON Decode error into something actionable

I'm trying to improve my error handling when I don't get back any data in my JSON. The function below works fine without any issues except if I catch an error. I'm not sure of how to pass the error to prompt the user or is the way to handle the error if googlebusinessinfo is nil I likely have no data and pass a generic error?
Here's my function:
func getBusinessReviews(googleUrl: String, completion: #escaping (WelcomeReview?) -> ()) {
// Create URL
let url = URL(string: googleUrl)
guard let requestUrl = url else { fatalError() }
// Create URL Request
var request = URLRequest(url: requestUrl)
// Specify HTTP Method to use
request.httpMethod = "GET"
//Set HTTP Header
// Send HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// Specify HTTP Method to use
request.httpMethod = "GET"
print("(Google)Do we have any data: \(String(describing: data))")
// Check if Error took place
if let error = error {
print("Error took place \(error)")
return
}
// Read HTTP Response Status code
if let response = response as? HTTPURLResponse {
print("(Google)Response HTTP Status code: \(response.statusCode)")
}
// Convert HTTP Response Data to a simple String
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Googledata Response data string:\n \(dataString)")
}
if let googlebusinessdata = data {
do {
let googlebusinessinfo = try JSONDecoder().decode(WelcomeReview.self, from: googlebusinessdata)
print("googledata \(googlebusinessinfo)")
completion(googlebusinessinfo)
} catch
{
print("googledata error \(error.localizedDescription)")
let googebusinessinfoerror = error.localizedDescription
}
}
}
task.resume()
}
First of all you can improve the error handling immensely if you print error instead of error.localizedDescription in a Decoding catch block. The former shows the real error, the latter a meaningless generic message.
To answer your question use the generic Result type
func getBusinessReviews(googleUrl: String, completion: #escaping (Result<WelcomeReview,Error>) -> ()) {
// Create URL
let url = URL(string: googleUrl)
guard let requestUrl = url else { fatalError() }
// Create URL Request
var request = URLRequest(url: requestUrl)
// Specify HTTP Method to use
request.httpMethod = "GET"
//Set HTTP Header
// Send HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// Specify HTTP Method to use
request.httpMethod = "GET"
print("(Google)Do we have any data: \(String(describing: data))")
// Check if Error took place
if let error = error {
print("Error took place \(error)")
completion(.failure(error))
return
}
// Read HTTP Response Status code
if let response = response as? HTTPURLResponse {
print("(Google)Response HTTP Status code: \(response.statusCode)")
}
// Convert HTTP Response Data to a simple String
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Googledata Response data string:\n \(dataString)")
do {
let googlebusinessinfo = try JSONDecoder().decode(WelcomeReview.self, from: data)
print("googledata \(googlebusinessinfo)")
completion(.success(googlebusinessinfo))
} catch
{
print("googledata error:", error)
let googebusinessinfoerror = error.localizedDescription
completion(.failure(error))
}
}
}
task.resume()
}
and call it
getBusinessReviews(googleUrl: "Foo") { result in
switch result {
case .success(let info): print(info)
case .failure(let error): print(error)
}
}

unable to have a result while calling a rest API with Swift

I try to make a small command line program in swift 5 to call a Rest Web service
func callRest() {
URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)
let todoEndpoint: String = "http://myURL"
print (todoEndpoint)
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
exit(1)
}
// private let apiKey = "my API Key"
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = [
"Accept": "application/json",
"API-Key": "my API Key"
]
var session = URLSession.shared
session = URLSession(configuration: config)
let urlRequest = URLRequest(url: url)
let task = session.dataTask(with: urlRequest) { (data, response, error) in
print ("task")
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// check the status code
guard let httpResponse = response as? HTTPURLResponse else {
print("Error: It's not a HTTP URL response")
return
}
print (httpResponse)
}
task.resume()
// Response status
}
print ("debut")
callRest()
print("fin")
And I never see a result...
the line print ("task") is never display
Any help appreciate.
I think I don't really understand the task.resume usage...
(same code work in a playground)

function with dataTask returning a value

I wan't to check if my url statusCode equals to 200, I created a function returning a Boolean if the statusCode equals to 200, I'm using a dataTask, but I don't know how to return a value:
class func checkUrl(urlString: String) -> Bool{
let urlPath: String = urlString
var url: NSURL = NSURL(string: urlPath)!
var request: NSURLRequest = NSURLRequest(url: url as URL)
var response: URLResponse?
let session = Foundation.URLSession.shared
var task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let data = data{
print("data =\(data)")
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
if httpResponse.statusCode == 200{
return true
} else {
return false
}
}
})
task.resume()
}
The returns in if else are returning an error:
Unexpected non-void return value in void function
in order to return value you should use blocks. Try declaring your function like this:
class func checkUrl(urlString: String, finished: ((isSuccess: Bool)->Void) {
let urlPath: String = urlString
var url: NSURL = NSURL(string: urlPath)!
var request: NSURLRequest = NSURLRequest(url: url as URL)
var response: URLResponse?
let session = Foundation.URLSession.shared
var task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let data = data{
print("data =\(data)")
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
if httpResponse.statusCode == 200{
finished(isSuccess: true)
} else {
finished(isSuccess: false)
}
}
})
task.resume()
}
And then call it like this:
checkUrl("http://myBestURL.com", finished { isSuccess in
// Handle logic after return here
})
Hope that this will help.
Consider semaphore if you want to keep your original return pattern.
func checkUrl(urlString: String) -> Bool {
if let url = URL(string: fileUrl) {
var result: Bool!
let semaphore = DispatchSemaphore(value: 0) //1. create a counting semaphore
let session = URLSession.shared
session.dataTask(with: url, completionHandler: { (data, response, error) in
result = true //or false in case
semaphore.signal() //3. count it up
}).resume()
semaphore.wait() //2. wait for finished counting
return result
}
return false
}
Swift4, work in my case
Try to add guard let data = data else { return } in dataTask like:
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
print("get some data")
}.resume()
You're returning a value from a Void function that is the completionHandler closure of dataTask(_:, _:)
Regarding your code, there is something wrong: you can't return that value because it's executed on a different thread, it's an asynchronous operation. Please take a look at this thread: Returning data from async call in Swift function

Swift add params to request

I'm trying to add an email and a password to a NSURLSession request like this:
#IBAction func btnLogin(sender: AnyObject) {
let email = txtEmail.text
let password = txtPassword.text
let data = ["email": email!, "password": password!] as Dictionary<String, String>
let session = NSURLSession.sharedSession()
let url = NSURL(string: "http://temp.com/api/v1/login")!
let request = NSMutableURLRequest(URL: url)
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(data, options: [])
}
catch {
print("error!")
}
request.HTTPMethod = "POST"
let task = session.dataTaskWithRequest(request) {
(data, response, error) in
guard let response = response as? NSHTTPURLResponse else {print(error); return}
if response.statusCode == 200 {
print("ingelogd!")
}
self.errorLogin()
}
task.resume()
}
error:
2016-07-03 17:54:04.890 EmployeeDirectory2[4693:133721] *** Assertion failure in -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.60.7/Keyboard/UIKeyboardTaskQueue.m:386
2016-07-03 17:54:04.894 EmployeeDirectory2[4693:133721] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'
But when I try this it crashes. How can I add parameters appropriately?
The crash is probably coming from the call to self.errorLogin() call, which tries to do some UI task outside the main thread. You should be able to fix it like this:
let task = session.dataTaskWithRequest(request) {
(data, response, error) in
guard let response = response as? NSHTTPURLResponse else {print(error); return}
if response.statusCode == 200 {
print("ingelogd!")
}
// Add this:
dispatch_async(dispatch_get_main_queue()) {
self.errorLogin()
}
}

Making an API call in Swift 2.0

I've been scouring examples looking to pull some ideas together, I've come up with this although I'm not getting any output. It never enters the do which leads me to believe I have an issue with my call.
Can anyone shed some light on this for me or lead me to an appropriate location with more information on API calls in swift 2.0? Examples of this are quite sparse.
let url : String = "http://www.fantasyfootballnerd.com/service/nfl-teams/json/test/"
let request : NSMutableURLRequest = NSMutableURLRequest()
request.URL = NSURL(string: url)
request.HTTPMethod = "GET"
print("Start")
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
do {
let jsonResult: NSDictionary! = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers) as? NSDictionary
print("In method")
if (jsonResult != nil) {
// process jsonResult
print("Data added")
} else {
print("No Data")
// couldn't load JSON, look at error
}
}
catch {
print("Error Occured")
}
}
You're missing just one thing. You need to start the request:
// call this after you configure your session
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
// process results
}.resume()