Passing JSON Decode error into something actionable - swift

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

Related

URLSession with URLCredential

I would like to get data from server, but credential (username, password. Not Basi Authentification) is necessary to get it. Therefore, I try to use URLSession with URLCredential as following:
https://developer.apple.com/documentation/foundation/url_loading_system/handling_an_authentication_challenge
However, I could not understand this document...
So could you tell me how to use URLSession with URLCredential?
I tried URLSession like:
func startLoad() {
let url = URL(string: "https://www.abcde-test-url.com/")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print("client error: \(error.localizedDescription) \n")
return
}
guard let data = data, let response = response as? HTTPURLResponse else {
print("no data or no response")
return
}
if response.statusCode == 200 {
print(data)
} else {
print("error status code: \(response.statusCode)\n")
}
}
task.resume()
}
But I couldn't understand the usage with URLCredential.

POST to Web API always returns nil

I'm just learning Web APIs with Swift.
And I've done as follows
Make some basic configuration
let session = URLSession(configuration: .default)
// Prepare URL #the URL is virtual now
let url = URL(string: "http://something.com/api")
guard let requestUrl = url else { fatalError() }
// Prepare URL Request Object
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
Set Post parameter
let parameters: [String: String] = [
"user_ID": "1",
]
let jsonData = try JSONEncoder().encode(parameters)
request.httpBody = jsonData
Take request to our web api
// Perform HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
// Check for Error
if let error = error {
print("Error took place \(error)")
return
}
guard let data = data else { return }
do {
let myEnterprise = try JSONDecoder().decode(Enterprise.self, from: data)
print("Response data:\n \(myEnterprise)")
print("Response data:\n \(myEnterprise.returnData)")
} catch let jsonErr {
print(jsonErr)
}
}
task.resume()
myEnterprise is always nil. Can anyone help me?

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)

How to show message when Task fails due to Timeout

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

Error in POST Request in swift

I wanted to be a type of variable to send to the dictionary server but But on the line I was faced with the problem let task = session.dataTaskWithRequest(todosUrlRequest) error : Cannot convert value of type 'NSURL' to expected argument type 'NSURLRequest'
I had two questions
1) What is this error?
2)Is there a procedure that I used for POST is that right? doesn't need anything else. ??
thank you for help
func data_request (){
let url = "http://sample.com/api/Flight/GetTicketInformation"
guard let todosUrlRequest = NSURL(string: url) else {
print("Error: cannot create URL")
return
}
let request = NSMutableURLRequest(URL: todosUrlRequest)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let newTodo = ["Roundtrip": roundTrip,
"OneWay": oneWay,
"MultiWay": multiWay,
"Adult": numberAdults,
"Child": numberchild,
"Baby": numberinfant,
"SourceCityId": cityIDOrigin,
"DestinationCityId": cityIDPurpose,
"DepartingDate": raftDate,
"ReturningDate": bargashtDate ]
let jsonTodo: NSData
do {
jsonTodo = try NSJSONSerialization.dataWithJSONObject(newTodo, options: [])
request.HTTPBody = jsonTodo
} catch {
print("Error: cannot create JSON from todo")
return
}
request.HTTPBody = jsonTodo
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(todosUrlRequest) {
(data, response, error) in
guard let responseData = data else {
print("Error: did not receive data")
return
}
guard error == nil else {
print("error calling POST on /todos/1")
print(error)
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let receivedTodo = try NSJSONSerialization.JSONObjectWithData(responseData,
options: []) as? [String: AnyObject] else {
print("Could not get JSON from responseData as dictionary")
return
}
print("The todo is: " + receivedTodo.description)
} catch {
print("error parsing response from POST on /todos")
return
}
}
task.resume()
}
request instead of todosUrlRequest on the line let task = session.dataTaskWithRequest(todosUrlRequest)
for the second question, no idea . sorry
I can recommend you Alamofire for all requests, instead of writing all code on your own.
https://github.com/Alamofire/Alamofire