I am building an app that checks domain availability. Right now, I am using the goDaddy API; according to goDaddy, the way to check URL availability is by the following cURL command:
curl -X GET -H "Authorization: sso-key {API_KEY}:{API_SECRET}" "https://api.godaddy.com/v1/domains/available?domain=example.guru"
Right now, I am trying to translate that cURL command to Swift. I am using Alamofire; however, when I make a request, an error as the following shows up:
Credentials must be specified
I was wondering how to solve this problem. Here is my current code:
class ViewController: UIViewController {
let key = "KEYKEYKEY"
let secret = "SECRETSECRET"
var headers: HTTPHeaders = [:]
override func viewDidLoad() {
super.viewDidLoad()
let credential = URLCredential(user: key, password: secret, persistence: .forSession)
Alamofire.request("https://api.godaddy.com/v1/domains/available?domain=example.guru")
.authenticate(usingCredential: credential)
.responseJSON { response in
debugPrint(response)
}
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I know that this is not using Alamofire but this is how I would achieve it.
import PlaygroundSupport
import Foundation
let key = "2s7Ymz8gu7_8XuTEeMFVmxJBLmyNNL4n8"
let secret = "8XuXAs8K37ejtYqvsEue2p"
let url = URL(string: "https://api.ote-godaddy.com/v1/domains/available?domain=example.guru&checkType=FULL")
var request = URLRequest(url: url!)
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("sso-key \(key):\(secret)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
print(error!)
return
}
guard let data = data else {
print("Data is empty")
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
print(json)
}
task.resume()
PlaygroundPage.current.needsIndefiniteExecution = true
Related
Hi I want to integrate the Curl POST api in my code I don't have any idea about this could any please guide me how to integrate this in swift language
The below web service call I have integrate in my code, Have tried but didn't get the result
curl -X POST http://stf.rortechnologies.com/api/session.js --data '{"user": {"email":"XXXXXX", "password":"XXXXXX"}}' -H "Content-Type:application/json"
let parameters = ["email":"admin.test#stf.com", "password":"password"]
let header = ["user": parameters]
//create the url with URL
let url = URL(string: "http://stf.rortechnologies.com/api/session.js")! //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 = "POST" //set http method as POST
do {
request.httpBody = try JSONSerialization.data(withJSONObject: header, options: .prettyPrinted) // pass dictionary to nsdata object and set it as request body
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
//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] {
print(json)
// handle json...
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
here am getting the null response
App Transport Security (ATS)
You are calling a http url and not a https url. In production always https should be used. This is enforced by iOS.
For testing purposes one can declare exceptions in the info.plist, the documentation can be found here:
https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33
JSON Encoding/Decoding
In Swift there is a convenient way to encode/decode JSON with JSONEncode/JSONDecoder. A simple solution might look like the one below.
Define Parameter Structs
struct Login: Encodable {
let user: User
}
struct User: Encodable {
let email: String
let password: String
}
Define Return Struct
struct Result: Decodable {
//add expected JSON fields here
}
Rest Call
private func makeRestCall() {
let login = Login(user: User(email: "admin.test#stf.com", password: "password"))
guard let loginJson = try? JSONEncoder().encode(login) else { return }
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
guard let url = URL(string: "<add some valid url here>") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = loginJson
let dataTask = session.dataTask(with: request) { (data, response, error) in
if let response = response as? HTTPURLResponse {
if response.statusCode == 200 {
guard let data = data else {
print ("call failed - no data returned")
return
}
guard let result = try? JSONDecoder().decode(Result.self, from: data) else {
print ("json decoding failed")
return
}
print ("call succesfully returned \(result)")
} else {
print ("call failed with status \(response.statusCode)")
}
} else {
print ("call failed: no http response")
}
}
dataTask.resume()
}
Check in a HTTPS Proxy
To make sure that you send the correct data, you could use a HTTPS proxy software. There it would look like this:
I'm trying to make an REST API call to a universal devices hub to turn a switch on. It seems like the call is going through, however I am getting an error that says I need credentials, which makes sense because there are credentials needed to get into the interface. However I am not sure how to make this work.
My code is the following
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var lightOn: UIButton!
#IBAction func lightOn(_ sender: Any) {
guard let url = URL(string: "http://0.0.0.0/rest/nodes/ZW002_1/cmd/DFON") else { return }
let userCredential = URLCredential(user: "admin",
password: "admin",
persistence: .permanent)
URLCredentialStorage.shared.setDefaultCredential(userCredential, for: protectionSpace)
// create URL session ~ defaulted to GET
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
// optional chaining to make sure value is inside returnables and not not
if let response = response {
print(response)
}
if let data = data {
// assuming the data coming back is Json -> transform bytes into readable json data
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print("error")
}
}
}.resume() // if this is not called this block of code isnt executed
}
}
I tried piecing together a couple examples online, and the ones I have seen use protectionSpace but when I use it the code returns:
Use of unresolved identifier 'protectionSpace'
Also overall whenever I actually run the simulator I get this exact error:
2017-12-26 13:28:58.656122-0600 hohmtest[6922:1000481] CredStore - performQuery - Error copying matching creds. Error=-25300, query={
atyp = http;
class = inet;
"m_Limit" = "m_LimitAll";
ptcl = http;
"r_Attributes" = 1;
sdmn = "/";
srvr = "192.168.1.73";
sync = syna;
}
<NSHTTPURLResponse: 0x60400042a3e0>
{ URL:
http://192.168.1.73/rest/nodes/ZW002_1/cmd/DON/ } { Status Code: 401,
Headers {
"Cache-Control" = (
"max-age=3600, must-revalidate"
);
Connection = (
"Keep-Alive"
);
"Content-Length" = (
0
);
"Content-Type" = (
"text/html; charset=UTF-8"
);
EXT = (
"UCoS, UPnP/1.0, UDI/1.0"
);
"Last-Modified" = (
"Tue, 26 Dec 2017 11:26:15 GMT"
);
"Www-Authenticate" = (
"Basic realm=\"/\""
);
} }
error
This solution worked for me. This is how I called a REST API that required a username and password. For those wondering, I put this code inside my IBAction button and didn't have to do anything else other than making the button.
let username = "admin"
let password = "admin"
let loginData = String(format: "%#:%#", username, password).data(using: String.Encoding.utf8)!
let base64LoginData = loginData.base64EncodedString()
// create the request
let url = URL(string: "http:/rest/nodes/ZW002_1/cmd/DFON")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Basic \(base64LoginData)", forHTTPHeaderField: "Authorization")
//making the request
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse {
// check status code returned by the http server
print("status code = \(httpStatus.statusCode)")
// process result
}
}
task.resume()
********* EXTRA NOTE *************
If yours does not have a username and password and you are trying to call a REST API call in swift here is some code that can help you! BOTH ARE GET REQUESTS!
#IBAction func onGetTapped(_ sender: Any) {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { return }
// create URL session ~ defaulted to GET
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
// optional chaining to make sure value is inside returnables and not not
if let response = response {
print(response)
}
if let data = data {
// assuming the data coming back is Json -> transform bytes into readable json data
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print("error")
}
}
}.resume() // if this is not called this block of code isnt executed
}
all.
I can get authentication token by login. but I can authenticate on my server by swift and alamofire.
this is Postman. as you see, if i have token, i can be authenticated on myserver.
this is swift viewcontroller.
import UIKit
import Alamofire
import SwiftyJSON
import KeychainAccess
class ViewController: UIViewController {
let authLoginUrl = "http://ec2-52-79-155-29.ap-northeast-2.compute.amazonaws.com:8000/rest-auth/login/"
let keychain = Keychain(service: "wanote")
let projectUrl = "http://ec2-52-79-155-29.ap-northeast-2.compute.amazonaws.com:8000/api/user/ryulstory"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let username = "username"
let password = "1234!"
self.doAuth(username: username, password: password)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func doAuth(username:String, password:String) {
let params = ["username": username, "password": password]
var authToken = Alamofire.request(self.authLoginUrl, method: .post, parameters: params)
authToken.responseJSON { response in
let statusCode = response.response?.statusCode ?? 0
switch statusCode {
case 200...299:
let jsonData = JSON(response.result.value)
if let token = jsonData["key"].string{
self.keychain["token"] = token
self.getProjects()
}
case 400...499:
print("Server responded no")
case 500...599:
print("Server error")
default:
print("There was an error with your request")
}
}
}
func getProjects(){
if let token = self.keychain["token"] {
if let Purl = URL(string: self.projectUrl){
var mutableUrlRequest = URLRequest(url: Purl)
mutableUrlRequest.httpMethod = "GET"
mutableUrlRequest.setValue("Token " + token, forHTTPHeaderField: "Authorization")
var manager = Alamofire.SessionManager.default
var getProjectsRequest = manager.request(mutableUrlRequest)
getProjectsRequest.responseJSON { response in
print(response.data)
}
}
} else {
print("no token")
}
}
}
I checked getting token by function doAuth. it is correctly operating.
function getprojects makes error status code 401.
I think there are problem in function getprojects. but i can't find it.
could you help me?
Best regards.
It goes to 401 because you are sending the request without the header authorization.
With Alamofire you can set the header Authorization like this.
let url = "URL_LOGIN"
//Get token logic
let token = ""
let headers = ["Authorization": "Token \(token)"]
let params = ["user": "", "pass":""] //This goes in the body of the request
Alamofire.request(url, method: .post, parameters: params, encoding: URLEncoding.default, headers: headers).responseJSON { (response) in
if let value = response.result.value {
print(value)
}
}
With this you'll send the headers in the request to get what you want.
I think this part have the header authorization.
is it wrong?
mutableUrlRequest.httpMethod = "GET"
mutableUrlRequest.setValue("Token " + token, forHTTPHeaderField: "Authorization")
var manager = Alamofire.SessionManager.default
var getProjectsRequest = manager.request(mutableUrlRequest)
I've made a little App in Swift where a user can search the Spotify database for songs. I am using the Web API Console > Search for an Item. My problem is the new OAuth system where you have to sign-in and all that stuff. My authorization is ok, but when I'm trying to get an access token with the following code, it's returning me the following error: {"error":"server_error","error_description":"Unexpected status: 400"}. My code is:
let keys = "<MY_APPLICATION_KEYS>"
let url = NSURL(string: "https://accounts.spotify.com/api/token")
let session = URLSession.shared
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "POST"
request.setValue("Basic \(keys)", forHTTPHeaderField: "Authorization")
request.setValue("client_credentials", forHTTPHeaderField: "grant_type")
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
guard let _: Data = data, let _: URLResponse = response, error == nil else {
print(error!)
return
}
let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("Data: \(dataString!)")
self.parseData(JSONData: data!)
}
task.resume()
}
var accessToken = ""
func parseData(JSONData : Data) {
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! JSONStandard
if let token = readableJSON["access_token"] as? String {
accessToken = token
}
print("Access Token: \(accessToken)")
updateTokenInFirebase()
}
catch{
print(error)
}
Any help would be very appreciated, thank you very much in advance!
Documentation of the Web API: Web API Link
I am using on the Client Credentials Flow the first method.
I know it's been ~1 year since you posted this but I had the same issue and after a few tries was able to get it. You can test this in Playground.
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
if let url = URL(string: "https://accounts.spotify.com/api/token") {
var postRequest = URLRequest(url: url)
postRequest.httpMethod = "POST"
let bodyParams = "grant_type=client_credentials"
postRequest.httpBody = bodyParams.data(using: String.Encoding.ascii, allowLossyConversion: true)
let id = "your client id"
let secret = "your secret"
let combined = "\(id):\(secret)"
let combo = "\(id):\(secret)".toBase64()
postRequest.addValue("Basic \(combo)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: postRequest) { (data, response, error) in
guard let data = data else {
return
}
print(String(data: data, encoding: String.Encoding.utf8)!)
}
task.resume()
}
extension String {
func fromBase64() -> String? {
guard let data = Data(base64Encoded: self) else {
return nil
}
return String(data: data, encoding: .utf8)
}
func toBase64() -> String {
return Data(self.utf8).base64EncodedString()
}
}
I know this is really late, but the issue is with this line:
request.setValue("client_credentials", forHTTPHeaderField: "grant_type")
According to the authorization guide, this should be in the body of the request, not the headers.
I have a server for storing username and password data in my application. When testing the app I had everything save to the device locally using NSDefaults, but now that the app is close to being fully launched, I am trying to save them to the server instead, as it is safer that way for the user's information.
When I had it save to NSDefaults, it was easy and short work. Now however, I am trying to POST the data to the server and keep getting build errors. What do I need to change for this to work? Am I not fully understanding how POST and GET works? Thanks. Using Swift 2 as of right now, not my choice, I prefer 3, but my boss isn't letting us update it yet.
The current error is coming from the POST USER DATA TO SERVER section, where xcode claims that userNmeTxt cannot be converted into NSData. Thank you in advance.
EDIT: Error is on line 87: "Cannot convert value of type UITextField! to expected argument type NSData!"
import UIKit
class UserNameViewController: AuthorizationViewController {
#IBOutlet weak var userNameTxt: UITextField!
#IBOutlet weak var continueBtn: UIButton!
var userModel: ProfileModel!
//MARK: - SYSTEMS METHODS
override func viewDidLoad() {
super.viewDidLoad()
userNameTxt.delegate = self
userNameTxt.autocapitalizationType = .Sentences
setEnabledButton()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBarHidden = false
self.navigationItem.leftBarButtonItem = getBackButton()
self.title = ""
}
override func viewWillDisappear(animated: Bool) {
self.navigationController?.navigationBarHidden = true
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
continueBtn.layer.cornerRadius = 10
}
override func popToRoot(sender:UIBarButtonItem){
self.navigationController!.popViewControllerAnimated(true)
}
//MARK: - CHECK FOR AVALABILITY
func setEnabledButton(){
if userNameTxt.text == "" {
continueBtn.backgroundColor = UIColor.lightGrayColor()
} else {
continueBtn.backgroundColor = UIColor(colorLiteralRed: 63.0/255.0, green: 220.0/255.0, blue: 236.0/255.0, alpha: 1.0)
}
continueBtn.userInteractionEnabled = userNameTxt.text != ""
}
//MARK: - POST USER DATA TO SERVER
func postData(url: String, params: Dictionary<String, String>, completionHandler: (data: NSData?, response: NSURLResponse?, error: NSError?) -> ()) {
// Indicate download
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
let url = NSURL(string: "myPlaceholderURLgoesHere")!
// print("URL: \(url)")
let request = NSMutableURLRequest(URL: url)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// Verify downloading data is allowed
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: [])
} catch let error as NSError {
print("Error in request post: \(error)")
request.HTTPBody = nil
} catch {
print("Catch all error: \(error)")
}
// Post the data
let task = session.dataTaskWithRequest(request) { data, response, error in
completionHandler(data: userNameTxt, response: userModel, error: error)
// Stop download indication
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
// Stop download indication
}
task.resume()
}
//MARK: - SEGUE
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toPassword"{
let controller = segue.destinationViewController as! PasswordViewController
controller.userModel = userModel
}
}
//MARK: - IB ACTIONS
#IBAction func continuePressed(sender: AnyObject) {
userModel.userNickName = userNameTxt.text!
performSegueWithIdentifier("toPassword", sender: self)
}
}
extension UserNameViewController: UITextFieldDelegate{
func textFieldDidEndEditing(textField: UITextField) {
self.setEnabledButton()
}
}
There are a couple of things you need to change.
userNameTxt is not the username, it's the UITextField containing the username. The text you need is userNameTxt.text?
If the function is expecting Data, you have to convert your text to Data first
let task = session.dataTaskWithRequest(request) { data, response, error in
completionHandler(data: userNameTxt.text?.data(using: .utf8), response: userModel, error: error)
I assume you have to send the data to the server.
If you don't have too you can save the data in keychain access, see: SO: Keychain Access
In order to resolve the error please edit the question with the error message and line of code (if possible).
I would suggest that you use Alamofire for POST/GET (REST). To use Alamofire you need basic knowledge of Cocoapods. It's better in the long term.
NOTE: There can be two possible error outcomes when you make the request.
1) Incorrect data format or bug from your side
2) Server error due backend bug from server side.
The data can be sent from your device with POST where the data is in the BODY or HEADER of the request. Usually it is in the body (parameters in the alamofire methods).
Here is an example:
import Alamofire
...
// MARK:- Login Feature - Universal Met for login
internal static func loginWith(serverUrl: String, parameters: [String: AnyObject]?, headers: [String: String]?, notificationName: String, serviceType: LoginService)
{
Alamofire.request(.POST, serverUrl, parameters: parameters, headers: headers).responseJSON
{ (response) in
print("\n Login feature - \n")
print(" Login url - \(serverUrl)\n")
print(" Login parameters - \(parameters)\n")
print(" Login notificationName - \(notificationName)\n")
print(" Login response - \(response)\n")
EXCDataParserH.parseResponseFrom(ServiceType.Login(type: serviceType),
operation: nil,
data: response.data,
notification: notificationName)
}
}
Instead of writing the whole thing every time you make a server request, try to do as follows:
import Foundation
import SystemConfiguration
class HTTPHelper{
class func httpPostDataDic(postURL:NSURL,postString:NSString,completionHandler:#escaping (NSDictionary?, NSError?) -> Void ) -> URLSessionTask{
var responseResultData: NSDictionary = NSDictionary()
let request = NSMutableURLRequest(url:postURL as URL);
request.httpMethod = "POST";// Compose a query string
request.httpBody = postString.data(using: String.Encoding.utf8.rawValue);
print(request)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil
{
print("error=\(error)")
completionHandler(nil, error as NSError?)
return
}
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
if let responseString = responseString {
print("responseString = \(responseString)")
}
do {
let myJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
responseResultData=myJSON!
completionHandler(responseResultData, nil)
} catch {
print(error)
}
}
task.resume()
return task
}
}
Now whenever you need to make a server POST request,in your ViewController class, do as follows:
//Requesting server
func requestServer() -> Void{
let postvariable = "Value"
let url = URL(string: "your url")!
let postString = "postkey=\(postvariable)"
HTTPHelper.httpPostDataDic(postURL: url as NSURL, postString: postString) {
(responseResult, error) -> Void in
DispatchQueue.main.async {
if error != nil{
print(error ?? "unknown")
}
else{
print(responseResult ?? "unknown result")
//Parse your response
self.parseResult(result: responseResult!);
}
}
}
}
May I ask you one thing that I didn't understand in your question.
How exactly would you save the login credentials in a server? I mean, if you save the login credentials in the server, how would you authenticate user access to these saved credentials?