unable to have a result while calling a rest API with Swift - 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)

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.

Swift: How to use HTTPS proxy

I have a paid proxy (HTTP/HTTPS/SOCKS).
I can make requests with HTTP scheme, but can't with HTTPS.
I always get en error:
Error Domain=kCFErrorDomainCFNetwork Code=311 "(null)" UserInfo={_NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <69F444DF-8C0E-4F4B-B723-C7BCD72B6C02>.<1>, _kCFStreamErrorDomainKey=4, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <69F444DF-8C0E-4F4B-B723-C7BCD72B6C02>.<1>"
), _kCFStreamErrorCodeKey=-2097}
I need to get and parse a few web pages from one concrete domain. I'm trying to use HTTP scheme, but it also fails (i guess due to redirect to HTTPS)
So, my goal is make HTTPS request via proxy server.
My code below:
Session configuration:
private func getProxySessionConfigration() -> URLSessionConfiguration {
let login = ConfigurationData.proxy.login
let password = ConfigurationData.proxy.password
let sessionConfiguration = URLSessionConfiguration.default
let userPasswordString = "\(login):\(password)"
let userPasswordData = userPasswordString.data(using: String.Encoding.utf8)
let base64EncodedCredential = userPasswordData!.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
let authString = "Basic \(base64EncodedCredential)"
sessionConfiguration.httpAdditionalHeaders = ["Authorization" : authString]
sessionConfiguration.connectionProxyDictionary = [
"HTTPEnable": true,
"HTTPPort": ConfigurationData.proxy.port,
"HTTPProxy": ConfigurationData.proxy.host,
"HTTPSEnable": true,
"HTTPSPort": ConfigurationData.proxy.port,
"HTTPSProxy": ConfigurationData.proxy.host
]
return sessionConfiguration
}
Request function:
public func getPage(_ url: URL, completion: #escaping (String?, RequestError?) -> Void) {
let session = URLSession(configuration: getProxySessionConfigration())
let task = session.dataTask(with: url) { (data, response, error) in
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
if let error = error {
completion(nil, .requestFailed(error))
} else {
completion(nil, .unknown(response))
}
return
}
if let data = data {
print(httpResponse)
if let urlContent = NSString(data: data, encoding: String.Encoding.utf8.self.rawValue) {
completion(urlContent as String, nil)
} else {
completion(nil, .dataDecodeFailed(data))
}
} else {
completion(nil, .noData)
}
}
task.resume()
}
Function call:
let requestAvito = AvitoProjectPlaygroud_Sources.RequestAvito()
let url = URL(string: "https://www.avito.ru/krasnodar/vakansii/voditel_v_yandeks_taksi_zarabotok_ot_1388074044")!
requestAvito.getPage(url) { (htmlString, error) in
if let error = error {
switch error {
case .noData: print("ERROR: no data")
case .unknown(let response):
print("ERROR: unknown error")
if let response = response { print(response) }
case .urlFailed: print("ERROR: url generating failed")
case .requestFailed(let returnedError): print("ERROR: \(returnedError)")
case .dataDecodeFailed(let data):
print("ERROR: data decoding failed")
print(data!)
}
return
}
if let htmlString = htmlString {
// print(htmlString)
print(String(htmlEncodedString: htmlString))
} else {
print("NO DATA")
}
}
For your issue first of all (Before ANYTHING else, make sure your proxy handle HTTPS proxying), if no then does not matter the code you put in.
To handle HTTPS Proxying there is 2 keys to use that it seems you are using it. One thing I can see is that you are using true as boolean, etc.
As per my experience I always used value such numeric or string lateral.
Make sure that data is passed as String / Int (true -> 1) etc. That could help.
Sometimes it can make some issues with those assumptions that true "will" be converted as 1.
Here below an example on how I use it:
let request = URLRequest(url: URL(string: url)!)
let config = URLSessionConfiguration.default
config.requestCachePolicy = URLRequest.CachePolicy.reloadIgnoringLocalCacheData
config.connectionProxyDictionary = [AnyHashable: Any]()
config.connectionProxyDictionary?[kCFNetworkProxiesHTTPEnable as String] = 1
config.connectionProxyDictionary?[kCFNetworkProxiesHTTPProxy as String] = "xxx.yyy.zzz.bbb"
config.connectionProxyDictionary?[kCFNetworkProxiesHTTPPort as String] = 8888
config.connectionProxyDictionary?[kCFStreamPropertyHTTPSProxyHost as String] = "xxx.yyy.zzz.bbb"
config.connectionProxyDictionary?[kCFStreamPropertyHTTPSProxyPort as String] = 8888
So in short :
Verify first your proxy (making sure https proxying is handled)
and then pass data as above
it may solve your issue hopefully.

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

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?

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