Swift iOS15 async/await + basic authentication + http request slow(er) - ios

I am using the below code for basic http authentication. It is noticeably slower than when I wasn't using authentication (the below is called around 30 times)
Are there any speed optimization changes that could be made to the code ?
Thanks
struct PrixJSONService {
let passwordString = "user:password"
let configuration = URLSessionConfiguration.default
enum PrixJSONServiceError: Error {
case failed
case failedToDecode
case invalidStatusCode
}
func fetchPrix(for stationId:String) async throws -> [Prix] {
let passwordData = passwordString.data(using:String.Encoding.utf8)!
let base64EncodedCredential = passwordData.base64EncodedString()
let authString = "Basic \(base64EncodedCredential)"
let session = URLSession(configuration: configuration)
configuration.httpAdditionalHeaders = ["Authorization" : authString]
let dataUrl = "https://xxxx.xx/~xx/xxxxxxxx/prix/\(stationId)/price.json"
let url = URL(string: dataUrl)!
var urlRequest = URLRequest(url: url)
urlRequest.setValue("Basic \(base64EncodedCredential)", forHTTPHeaderField: "Authorization")
urlRequest.httpMethod = "GET"
let (data, response) = try await session.data(for: urlRequest)
guard let response = response as? HTTPURLResponse,
response.statusCode == 200 else {
throw PrixJSONServiceError.invalidStatusCode
}
let decodedData = try JSONDecoder().decode([Price].self, from: data)
return decodedData
}
}

Related

How to implement the Bearer Token to validate the API url

I set up the API and all, the only thing is Bearer Token I couldn't find any information about any code on how to implement it so it can validate the URL I am using as API.
do I need to create new swift file just for bearer token or I can write the code to the API swift file "the code below is api file"
static let shared = APICaller()
private let baseURL = "http://000.000.000.000:3030/api/"
private init() {}
var vehicles = [Vehicles]()
func getVehicles(for id: String, IMEI: Int, completed: #escaping (Result<[Vehicles],Errors>) -> Void ){
let endpoint = baseURL + "GetVehicle/?UserIdentificationValue=346HIU4623UIHG3I3I&IMEI=216216123612"
guard let url = URL(string: endpoint) else {
completed(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.vehicles = try JSONDecoder().decode([Vehicles].self, from: data)
DispatchQueue.main.async {
completed(.failure(.invalidData))
}
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
Thanks In Advance
Also I am new to swift so I would appreciate if you can tell me my API code is correct or needs any fixes since its about receiving some car info and putting into a table view cell :)
I have attached the request including headers in which you need to pass Bearer token like did in below code
let headers = [
"content-type": "application/json",
"authorizetoken": "NjQzOPA2N0NDNDFAH4CNDk3R23F2FQUY0NjV3FFE=",
"cache-control": "no-cache",
]
let parameters = ["id":"123456789"] as [String : Any]
let postData = try? JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "Your URL")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 120.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as? Data
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData

Using Yelp Fusion API in swift app not authenticating, continuously receiving "VALIDATION_ERROR"

Code here
let link = "https://api.yelp.com/oauth2/token"
guard let url = URL(string: link) else { return }
// Headers
let headers = [
"content-type": "application/x-www-form-urlencoded"
]
guard let clientID = infoPlist(withKey: "YELP_API_CLIENT_ID"),
let clientSecret = infoPlist(withKey: "YELP_API_CLIENT_SECRET") else { return }
let body = "client_id=\(clientID)&client_secret=\(clientSecret)&grant_type=client_credentials"
var request = URLRequest.init(url: url)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = body.data(using: .utf8)
As far as I know this should be working. Based on everything I've read this is the proper process for authenticating with Yelp Fusion/v3.
You didn't post your entire code, but with some slight modifications your code works:
let appId = "xxx"
let appSecret = "yyy"
let link = "https://api.yelp.com/oauth2/token"
let url = URL(string: link)!
let bodyData = "client_id=\(appId)&client_secret=\(appSecret)&grant_type=client_credentials".data(using: .utf8)!
// Headers
let headers = [
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": "\(bodyData.count)"
]
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = bodyData
typealias JSON = [String:Any]
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data,
let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
print(error!)
return
}
if let responseJSON = try? JSONSerialization.jsonObject(with:data, options:[]),
let parsedJSON = responseJSON as? JSON {
let token = parsedJSON["access_token"]
let exipration = parsedJSON["expires_in"]
}
}.resume()

iOS Yelp OAuth Token Retrieval with URLRequest returning "client_id or client_secret parameters not found

I am attempting to retrieve an OAuth token to use Yelp's Fusion API from an iOS client using the native URL and URLRequest classes, but it is giving me this error in the "tokenInfo" variable:
client_id or client_secret parameters not found. Make sure to provide
client_id and client_secret in the body with the
application/x-www-form-urlencoded content-type
Here is my code:
func getToken(){
var yelpTokenEndpoint = "https://api.yelp.com/oauth2/token"
var tokenURL = URL(string: yelpTokenEndpoint)
let requestJSON: [String:String] = ["client_id":"Not showing actual client id", "client_secret":"Not Gonna Show My Actual Client Secret either","grant_type":"client_credentials"]
let requestData = try? JSONSerialization.data(withJSONObject: requestJSON)
print(try? JSONSerialization.jsonObject(with: requestData!, options: []))
var tokenURLRequest = URLRequest(url: tokenURL!)
tokenURLRequest.httpMethod = "POST"
tokenURLRequest.httpBody = requestData!
tokenURLRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
let tokenSession = URLSession.shared
let tokenTask = tokenSession.dataTask(with: tokenURLRequest) { (data, response, error) in
if error != nil {
print("error getting your access token")
print(error!.localizedDescription)
}
if let data = data{
do{
if let tokenInfo = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any]{
let token: String = tokenInfo["access_token"] as! String
print(token)
}
} catch {
print("Error converting to JSON")
}
}
}
tokenTask.resume()
}
And yes, I am certain that I put the right client credentials in. Any help would be much appreciated, thanks!
Try this ....
let clientId = "client_id"
let clientSecret = "client_secret"
let tokenURL = "https://api.yelp.com/oauth2/token"
let grantType = "client_credentials"
let url = NSURL(string: tokenURL as String );
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let request = NSMutableURLRequest(URL: NSURL(string: tokenURL)!)
request.HTTPMethod = "POST";
request.HTTPShouldHandleCookies = true
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let postString = "client_id=" + clientId + "&client_secret=" + clientSecret + "&grant_type=" + grantType
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task: NSURLSessionDataTask = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if let data = data {
let response = NSString(data: data, encoding: NSUTF8StringEncoding)
print(response)
}
}
task.resume()

How to make a NSURLSesssion GET request with cookies

I'm using the Pinterest SDK to download a Pinterest Pin's link, (sample link that I get back from the server: https://www.pinterest.com/r/pin/186195765822871832/4801566892554728205/77314e40aeb26c0dc412e9cfa82f8dccc401fdb2b9806a3fe17ba8bafdb50510).
About 5 days ago I started getting 404 errors in my NSURLSesssion when trying to access similar links that I'd pulled down from Pinterest.
A friend said that he believes Pinterest must now require cookies to access that link.
How can I configure my session so that I can use cookies and get a 200 response back from Pinterest?
UPDATED CODE:
import UIKit
import PlaygroundSupport
var url = URL(string: "https://www.pinterest.com/r/pin/186195765822871832/4801566892554728205/77314e40aeb26c0dc412e9cfa82f8dccc401fdb2b9806a3fe17ba8bafdb50510")
var getSourceURLFromPinterest: URLSessionDataTask? = nil
let sessionConfig: URLSessionConfiguration = URLSessionConfiguration.default
sessionConfig.timeoutIntervalForRequest = 30.0
sessionConfig.timeoutIntervalForResource = 30.0
let cookieJar = HTTPCookieStorage.shared
let cookieHeaderField = ["Set-Cookie": "key=value, key2=value2"]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: cookieHeaderField, for: url!)
HTTPCookieStorage.shared.setCookies(cookies, for: url, mainDocumentURL: url)
let session = URLSession(configuration: sessionConfig)
getSourceURLFromPinterest = session.dataTask(with: url! as URL) { (data: Data?, response: URLResponse?, error: Error?) -> Void in
if error != nil {
print("error is \(error)")
}
if response == nil {
print("no response")
} else if let _ = data {
//Config Request
let request = NSMutableURLRequest(
url: (response?.url)!,
cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: 30.0)
request.httpMethod = "HEAD"
var statusCode = Int()
let session = URLSession.shared
let checkURLForResponse = session.dataTask(with: request as URLRequest) {urlData, myResponse, responseError in
if let httpResponse = myResponse as? HTTPURLResponse {
statusCode = httpResponse.statusCode
switch statusCode {
case _ where statusCode < 500 && statusCode > 299 && statusCode != 405: //whitelisted 405 to exclude Amazon.com false errors
print("status code \(statusCode) for \(url)")
default:
break;
}
} else { print("***NO httpResponse for \(url)") }
}
checkURLForResponse.resume()
}
}
getSourceURLFromPinterest!.resume()
PlaygroundPage.current.needsIndefiniteExecution = true
The other answers may work generally, but specifically for me this is how I coded the request in order to get a response from Pinterest's server. Note that specifically what I am doing I think is related to a possible bug in Pinterest's server, see: https://github.com/pinterest/ios-pdk/issues/124
I commented out my personal Pinterest session ID
var cookieSession = String()
var cookieCSRFToken = String()
var myWebServiceUrl = URL(string: "https://www.pinterest.com/r/pin/186195765821344905/4801566892554728205/a9bb098fcbd6b73c4f38a127caca17491dafc57135e9bbf6a0fdd61eab4ba885")
let requestOne = URLRequest(url: myWebServiceUrl!)
let sessionOne = URLSession.shared
let taskOne = sessionOne.dataTask(with: requestOne, completionHandler: { (data, response, error) in
if let error = error {
print("ERROR: \(error)")
}
else {
print("RESPONSE: \(response)")
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("DATA: " + dataString)
} // end: if
var cookies:[HTTPCookie] = HTTPCookieStorage.shared.cookies! as [HTTPCookie]
print("Cookies Count = \(cookies.count)")
for cookie:HTTPCookie in cookies as [HTTPCookie] {
// Get the _pinterest_sess ID
if cookie.name as String == "_pinterest_sess" {
//var cookieValue : String = "CookieName=" + cookie.value as String
cookieSession = cookie.value as String
print(cookieSession)
}
// Get the csrftoken
if cookie.name as String == "csrftoken" {
cookieCSRFToken = cookie.value
print(cookieCSRFToken)
}
} // end: for
} // end: if
})
taskOne.resume()
var requestTwo = URLRequest(url: myWebServiceUrl!)
cookieSession = "XXXXXXXX"
cookieCSRFToken = "JHDylCCKKNToE4VXgofq1ad3hg06uKKl"
var cookieRequest = "_auth=1; _b=\"AR4XTkMmqo9JKradOZyuMoSWcMdsBMuBHHIM21wj2RPInwdkbl2yuy56yQR4iqxJ+N4=\"; _pinterest_pfob=disabled; _pinterest_sess=\"" + cookieSession + "\"; csrftoken=" + cookieCSRFToken as String
requestTwo.setValue(cookieRequest as String, forHTTPHeaderField: "Cookie")
let taskTwo = sessionOne.dataTask(with: requestTwo, completionHandler: { (data, response, error) in
if let error = error {
print("ERROR: \(error)")
}
else {
print("RESPONSE: \(response)")
} // end: if
})
taskTwo.resume()
PlaygroundPage.current.needsIndefiniteExecution = true
You can configure a cookie based session in the following way. Please let me know if you need any help. The below is just an example
let session: URLSession = URLSession.shared
session.dataTask(with: myUrlRequest { urlData, response, responseError in
let httpRes: HTTPURLResponse = (response as? HTTPURLResponse)!
let cookies:[HTTPCookie] = HTTPCookie.cookies(withResponseHeaderFields: httpRes.allHeaderFields as! [String : String], for: httpRes.url!)
HTTPCookieStorage.shared.setCookies(cookies, for: response?.url!, mainDocumentURL: nil)
if responseError == nil {
}else {
}
}.resume()
Feel free to suggest edits to make it better. Please let me know if the below doesn't work.
When you do an authentication with the server, the server gives out the cookies, which you receives in the header of the response. You can extract that and set as a cookie in the shared storage of cookies. So everytime you make a call, for those domain, the cookies will be shared and checked, and if valid, it will let you in.
let resp: HTTPURLResponse = (response as? HTTPURLResponse)!
let cookies:[HTTPCookie] = HTTPCookie.cookies(withResponseHeaderFields: resp.allHeaderFields as! [String : String], for: resp.url!)
HTTPCookieStorage.shared.setCookies(cookies, for: response?.url!, mainDocumentURL: nil)
In this, the cookies will be an array, which contain cookies in an array. An response may contain more than one cookies.
In case, if you are using third party framework to manage the HTTP requests like Alamofire then it will take cares of the cookie management itself.

Make a http request with basic authentication

I want to make an http request with basic auth. I tried to follow this topic : click here. But Xcode tell me
Bad Request after httpResponse
And I don't know why since this morning. Maybe anyone got a idea more interesting than I can find on the web ? :)
Here is my code :
func topRefresh(sender:AnyObject){
var list=Array<Notification>()
//credential
let credentialLogin = NSUserDefaults.standardUserDefaults().objectForKey("login") as! String
let credentialPassword = NSUserDefaults.standardUserDefaults().objectForKey("password") as! String
// set up the base64-encoded credentials
let loginString = NSString(format: "%#:%#", credentialLogin, credentialPassword)
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = loginData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
// create the request
let url = NSURL(string: jsonLink)!
let request = NSMutableURLRequest(URL: url)
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
request.HTTPMethod="GET"
let paramString="login="+credentialLogin
request.HTTPBody = paramString.dataUsingEncoding(NSUTF8StringEncoding)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
if (httpResponse.statusCode == 200) {
do{
if (data != nil){
self.notificationsDisplayed.removeAll()
let jsonDict = try NSJSONSerialization.JSONObjectWithData(data!,options: .AllowFragments)
list=self.parseJson(jsonDict)
if (self.notifications.count==0){
self.notifications=self.copyArray(list)
list.removeAll()
}else{
//compare new data with past data
while(list.count>0){
print(list.count)
let tmp=list.last
for notification in self.notifications {
if(tmp!.id==notification.id){
list.removeLast()
break
}else{
self.notifications.insert(tmp!, atIndex: 0)
list.removeLast()
break
}
}
}
}
self.orderByDate(&self.notifications)
self.notificationsDisplayed=self.copyArray(self.notifications)
self.applyFilter()
print("Data parsed")
}else{
print("Data is empty")
}
}catch {
print("Error with Json: \(error)")
}
}else{
print("HTTP Error")
}
self.refreshControl?.endRefreshing()
print("Finished")
}
task.resume()
}
I use the following in my code to create a default session that include the authentication:
static func defaultURLSession(username : String, password:String) -> NSURLSession {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let userPasswordString = "\(username):\(password)"
let userPasswordData = userPasswordString.dataUsingEncoding(NSUTF8StringEncoding)
let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
let authString = "Basic \(base64EncodedCredential)"
config.HTTPAdditionalHeaders = ["Authorization" : authString]
return NSURLSession(configuration: config)
}

Resources