Custom headers - Alamofire + Swift 3 - swift

I'm getting an error while trying to use this code:
func getRawJSON(method: String, paramether: String) {
let publicKey = "publicKeyHere"
let secretKey = "secretKeyHere
let APIURL = "https://www.bitmarket.pl/api2/"
let time = String(Int(NSDate().timeIntervalSince1970))
let query = NSURLComponents()
query.queryItems = [NSURLQueryItem(name: "method", value: method) as URLQueryItem,
NSURLQueryItem(name: "tonce", value: time) as URLQueryItem]
let requestString = query.query!
let requestData = Array(requestString.utf8)
let params = [
"method": method,
"tonce:": time
]
let hmac: Array<UInt8> = try! HMAC(key: secretKey.utf8.map({$0}), variant: .sha512).authenticate(requestData)
let hmacString = hmac.map{String(format: "%02X", $0)}.joined(separator: "").lowercased()
let URL = NSURL(string: APIURL)!
let mutableURLRequest = NSMutableURLRequest(url: URL as URL)
mutableURLRequest.httpMethod = "POST"
do {
mutableURLRequest.httpBody = try JSONSerialization.data(withJSONObject: paramether, options: JSONSerialization.WritingOptions())
} catch {
}
mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
mutableURLRequest.setValue(publicKey, forHTTPHeaderField: "API-Key")
mutableURLRequest.setValue(hmacString, forHTTPHeaderField: "API-Hash")
Alamofire.request(mutableURLRequest) //Here is a problem
}
Here is the error:
Argument type 'NSMutableURLRequest' does not conform to expected type 'URLRequestConvertible'
What am I doing wrong? Alamofire documentation says NSMutableURLRequest could conform to URLRequestConvertible.

Swift 3 defines URLRequest which conforms to the protocol URLRequestConvertible. You should use URLRequest instead of NSMutableURLRequest.
Refer to this discussion.

Related

Instance method requires that 'classname' conform to 'Decodable'

I'm trying to decode a JSON response as a custom type, that I believe conforms to Decodable.
These are the codable structs that I am using
struct ResultSet: Codable {
var name: String
var headers: [String]
var rowSet: [String]
}
struct Scoreboard: Codable {
var resultSets: [ResultSet]
}
And this is the code I'm using to get the JSON from the response
func loadNbaScoreboardData<Scoreboard>() -> Scoreboard {
//var data1: Data
let formatter = DateFormatter()
formatter.dateFormat = "MM/dd/yyyy"
let formattedDate = formatter.string(from: Date())
let url = URL(string: "https://stats.nba.com/stats/scoreboard/?GameDate=\(formattedDate)&LeagueID=00&DayOffset=100")
var request = URLRequest(url: url!)
request.httpMethod = "GET"
request.setValue("stats.nba.com", forHTTPHeaderField: "host")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("stats", forHTTPHeaderField: "x-nba-stats-origin")
request.setValue("x-nba-stats-origin", forHTTPHeaderField: "Referer")
var retData: Scoreboard
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else{ return }
do {
let decodedData = try JSONDecoder().decode(Scoreboard.self, from: data)
retData = decodedData
} catch {
fatalError(error)
}
}.resume()
return retData
}
The error I get is Instance method 'decode(_:from:)' requires that 'Scoreboard' conform to 'Decodable'
I'm following the dev documentation here too https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types
What am I doing wrong here?
EDIT: The Scoreboard struct can't be found. I've added the full method
In your code Scoreboard is a generic type (not the concrete type Scoreboard). You can fix the error by adding Codable conformance
func loadNbaScoreboardData<Scoreboard: Codable>() -> Scoreboard {
But the code won't work anyway because you cannot return something from an asynchronous task.
I recommend to make the function async
func loadNbaScoreboardData() async throws -> Scoreboard {
//var data1: Data
let formatter = DateFormatter()
formatter.dateFormat = "MM/dd/yyyy"
let formattedDate = formatter.string(from: Date())
let url = URL(string: "https://stats.nba.com/stats/scoreboard/?GameDate=\(formattedDate)&LeagueID=00&DayOffset=100")
var request = URLRequest(url: url!)
request.httpMethod = "GET"
request.setValue("stats.nba.com", forHTTPHeaderField: "host")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("stats", forHTTPHeaderField: "x-nba-stats-origin")
request.setValue("x-nba-stats-origin", forHTTPHeaderField: "Referer")
let (data, _ ) = try await URLSession.shared.data(for: request)
return try JSONDecoder().decode(Scoreboard.self, from: data)
}
It was the method declaration
It should be
func loadNbaScoreboardData() -> Scoreboard {
// code
}

HERE API OAuth Access Token Request - Swift

I am trying to request an OAuth access token from HERE API. It is working however I am receiving intermittent errors. Around 50% of the time I am receiving this response:
{"errorId":"ERROR-8638f6e6-4fe9-420e-a31c-600f3062105b","httpStatus":401,"errorCode":401300,"message":"Signature mismatch. Authorization signature or client credential is wrong.","error":"invalid_client","error_description":"errorCode: '401300'. Signature mismatch. Authorization signature or client credential is wrong."}
Here is my code:
let url = URL(string: "https://account.api.here.com/oauth2/token")!
//Credentials
let accessKeyId = "xxxxxxxxxxxxxxxx"
let accessKeySecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
//Base String
let timeStamp = String(Int(Date().timeIntervalSince1970))
let nonce = String(Int(Date().timeIntervalSince1970 * 1000))
let grant_type = "grant_type=client_credentials"
let oauth_consumer_key = "&oauth_consumer_key=" + accessKeyId
let oauth_nonce = "&oauth_nonce=" + nonce
let oauth_signature_method = "&oauth_signature_method=HMAC-SHA256"
let oauth_timestamp = "&oauth_timestamp=" + timeStamp
let oauth_version = "&oauth_version=1.0"
let paramsString = grant_type + oauth_consumer_key + oauth_nonce + oauth_signature_method + oauth_timestamp + oauth_version
let baseString = "POST&" + url.absoluteString.urlEncoded()! + "&" + paramsString.urlEncoded()!
//Key
let secret = accessKeySecret + "&"
let key = SymmetricKey(data: Data(secret.utf8))
//Signature
let signature = HMAC<SHA256>.authenticationCode(for: Data(baseString.utf8), using: key)
let baseEncodedSignature = Data(signature).base64EncodedString().urlEncoded()!
//Request
var request = URLRequest(url: url)
request.httpMethod = "POST"
//Request Headers
let authString = "OAuth oauth_consumer_key=\"\(accessKeyId)\",oauth_nonce=\"\(nonce)\",oauth_signature=\"\(baseEncodedSignature)\",oauth_signature_method=\"HMAC-SHA256\",oauth_timestamp=\"\(timeStamp)\",oauth_version=\"1.0\""
request.setValue(authString, forHTTPHeaderField: "Authorization")
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField:"Content-Type")
//Request Body
var requestBodyComponents = URLComponents()
requestBodyComponents.queryItems = [URLQueryItem(name: "grant_type", value: "client_credentials")]
request.httpBody = requestBodyComponents.query?.data(using: .utf8)
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = TimeInterval(20)
config.timeoutIntervalForResource = TimeInterval(20)
let urlSession = URLSession(configuration: config)
urlSession.dataTask(with:request, completionHandler: { (data, response, error) in
completion(data, response, error)
}).resume()
Extension for URL encoding...
public extension String {
func urlEncoded() -> String? {
addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)?
.replacingOccurrences(of: "&", with: "%26")
.replacingOccurrences(of: "=", with: "%3D")
}
}
Any ideas what is happening?
Many thanks
Hi would you please test below code?
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
var semaphore = DispatchSemaphore (value: 0)
let parameters = "{\"grantType\":\"client_credentials\",\"expiresIn\":86400}"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: "https://account.api.here.com/oauth2/token")!,timeoutInterval: Double.infinity)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("OAuth oauth_consumer_key=\"*************\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"1653538948\",oauth_nonce=\"fLlPzE4HBxJ\",oauth_version=\"1.0\",oauth_signature=\"vy9BT5dF3EBk0DR9JdxDu7StKeM%3D\"", forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
semaphore.signal()
return
}
print(String(data: data, encoding: .utf8)!)
semaphore.signal()
}
task.resume()
semaphore.wait()

Get value from callback function swift

Question I want to get the value returned from my ApiToken function so I can use it in another function. For some reason I can not get the value from this function it will not return anything. How could I return the value from my ApiToken function and use it in another function.
Here is my GetApiToken class with the ApiToken function
class GetApiToken {
public func ApiToken(link: String, completionBlock: #escaping (String) -> Void) -> Void
{
let url = URL(string: link)!
let jsonDict = ["username": "snow", "password": "ssssssssss"]
let jsonData = try! JSONSerialization.data(withJSONObject: jsonDict, options: [])
var request = URLRequest(url: url)
request.httpMethod = "post"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("error:", error)
return
}
do {
guard let data = data else { return }
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: AnyObject] else { return }
//self.token = json["access_token"] as? String ?? "x"
completionBlock((json["access_token"] as? String)!)
} catch {
print("error:", error)
}
}
task.resume()
}
}
Here is where I am trying to get the value
func getData(_ link:String)
{
let url = URL(string: link)!
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 20)
request.httpMethod = "GET"
var output = ""
GetApiToken().ApiToken(link: "http://localhost:5000/auth", completionBlock: { str in
output = str
})
request.addValue("JWT \(output)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type") ..........
It's an asynchronous call, so you need to put everything that will happen once the data has been retrieved in the completion callback
func getData(_ link:String)
{
let url = URL(string: link)!
var request = URLRequest(url: url,
cachePolicy: .reloadIgnoringCacheData,
timeoutInterval: 20)
request.httpMethod = "GET"
GetApiToken().ApiToken(link: "http://localhost:5000/auth",
completionBlock:
{ output in
request.addValue("JWT \(output)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
.......
})

Swift URLSession doesn't behave like Curl or HTTParty

Background
I am working on a swift project with particle.io setup two legged auth part. Basically it's a POST request.
My issue is I can get the correct response by CURL and HTTParty. (Like below) but withURLSession` the response is 404.
By CURL
curl -X POST -u "abcd:secret" -d password=true -d email="wwsd#gmail.com" https://api.particle.io/v1/orgs/xxx/customers
By HTTParty
require 'HTTParty'
def register_spark_two_legged_user(query)
return HTTParty.post("https://api.particle.io/v1/orgs/xxx/customers", body: query, basic_auth:{"username":"abcd","password":"secret"})
end
query = {"email":"wwsd#gmail.com", "no_password":true}
json = register_spark_two_legged_user query
p json
I want to do it in Swift:
func twoLegged() {
let urlString = "https://api.particle.io/v1/orgs/xxx/customers"
let parameters = ["email":"wwsd#gmail.com","no_password":true] as [String : Any]
let userName = "abcd"
let password = "secret"
let loginString = userName+":"+password
let loginData = loginString.data(using: String.Encoding.utf8)!
let base64LoginString = loginData.base64EncodedString()
let url = URL(string: urlString)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
URLSession.shared.dataTask(with: url) { (data: Data?, response: URLResponse?, error: Error?) in
if let e = error {
print(e.localizedDescription)
} else {
let json = try? JSONSerialization.jsonObject(with: data!, options: [])
debugPrint(response as Any)
print(json)
}
}.resume()
Did I miss something? Thanks for the help. Here's a link might useful: community.particle.io
EDIT I changed the httpBody still the same not work.
var comp = URLComponents()
comp.queryItems = [
URLQueryItem(name: "no_password", value: "true"),
URLQueryItem(name: "email", value: "wwsd#gmail.com"),
]
request.httpBody = comp.query?.data(using: String.Encoding.utf8)
request.setValue("application/x-www-form-urlencode", forHTTPHeaderField: "Content-Type")
The output is
Optional({
error = "Not Found";
ok = 0;
})
In curl you are sending the data out as application/x-www-form-urlencoded, i.e. in the form
no_password=true&email=wwsd#gmail.com
But in Swift you are sending off the data as JSON.
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
# wrong: form data is expected, not JSON.
You could format to application/x-www-form-urlencoded using URLComponents and URLQueryItem:
var comp = URLComponents()
comp.queryItems = [
URLQueryItem(name: "no_password", value: "true"),
URLQueryItem(name: "email", value: "wwsd#gmail.com"),
]
request.httpBody = comp.query?.data(using: .utf8)
Also you did not pass the request into URLSession...
URLSession.shared.dataTask(with: request) { ... }
// ^~~~~~~ you were passing `url`.

HTTP POST request in Swift

How do I post the request on iOS? Actually when I logged into Facebook it fetches the user informations like username, from where there are login (latitude, longitude). Is it possible to use api
Link: http://buddysin.aumkiiyo.com/fbc
My code is:
#IBAction func btnAPI(sender: UIButton)
{
//startConnection()
connectToWebAPI()
}
func connectToWebAPI()
{
//setting up the base64-encoded credentials
let id = "1620912344817986"
//let password = "pass"
let loginString = NSString(format: "%#:%#", id)
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = loginData.base64EncodedStringWithOptions(nil)
//creating the requestz
let url = NSURL(string: "http://buddysin.aumkiiyo.com/fbc")
var request = NSMutableURLRequest(URL: url!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession.sharedSession()
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let urlConnection = NSURLConnection(request: request, delegate: self)
request.HTTPMethod = "POST"
request.setValue(base64LoginString, forHTTPHeaderField: "Authorization")
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if (error != nil) {
println(error)
}
else {
// converting the data into Dictionary
var error: NSError?
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
println(jsonResult)
}
})
//fire off the request
task.resume()
}
while I run, the fatal error where displayed as
`fatal error: unexpectedly found nil while unwrapping an Optional value`
in the "jsonResult"
i think it is better to use Alomafire. As AFNetWorking in Objective-C it is a library which simplified a lot http request.
Visit this question to check for my post-request function (if
you don't want to use Alamofire for any reasons)
Visit this question to check for steps you need to do if you
want to add Alamofire to your XCode-project
If you need to get json-data from your server, use
SwiftyJSON. It's as simple as dragging SwiftyJSON.swift into
your project with checking "Copy items if needed" and using like
let jsonData = JSON(data: yourDataFromServer)
Also you can view this question to check out for steps to encode
json-post data to send it to server.
Hope I helped :)
You should find which varible due to this error:
for example data,
if let dataTemp = data as? NSDictionary {
}
FYI:
Here is a way of 'POST' method of AFNetworking in swift, below code should be in your connectToWebAPI method, wrap your url ready into NSURL.
let manager = AFHTTPRequestOperationManager(baseURL: NSURL(string: yourURL))
manager.POST("path", parameters: ["key":value], success: { (opeartion:AFHTTPRequestOperation!, data:AnyObject!) -> Void in
},failure: { (operation:AFHTTPRequestOperation!, error:NSError!) -> Void in
})
Tutorial to install AFNetworking.
https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking
It is quite easy to do with Alamofire
func postSomething(completionHandler: #escaping CompletionHandler){
let loginString = NSString(format: "%#:%#", id)
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = loginData.base64EncodedStringWithOptions(nil)
let headers: HTTPHeaders = [
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": "base64LoginString",
]
let parameters: Parameters = [
"parameter": value,
"parameter2": value2
]
Alamofire.request("http://buddysin.aumkiiyo.com/fbc", method: .post, parameters: parameters, encoding: URLEncoding.default, headers: SFAppServicesManager.sharedInstance.genericHeader()).responseJSON { response in
if let JSON = response.result.value {
let userDictionary = JSON as! NSDictionary
completionHandler(true)
} else {
completionHandler(false)
}
}
}