I have the following code but instead of outputting either of the statements; both statements are output:
EDITED:
After a lot of trial and error I am still no closer to a solution, I have condensed my code down to just the bare minimum and removed all other controls in an attempt to solve this and still not working, here is my minimal code that is attached to a simple single switch:
//
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var mySwitch: UISwitch!
#IBAction func buttonClicked(_ sender: Any) {
let switchOn = mySwitch.isOn
let endpoint = "http://10.0.1.147/setpins"
let urlString = endpoint + "?D2=" + (switchOn ? "0" : "1023")
mySwitch.setOn(!switchOn, animated:true)
var request = URLRequest(url: URL(string: urlString)!)
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
print("error=\(error)")
return
}
// You can print out response object
print("response = \(response)")
}
task.resume()
}
}
BUT: Still getting the output of each argument output at the same time..
response = Optional(<NSHTTPURLResponse: 0x17402d9e0> { URL: pttp://10.0.1.147/setpins?D1=0 } { status code: 200, headers {
"Access-Control-Allow-Origin" = "*";
Connection = close;
"Content-Length" = 7;
"Content-Type" = "text/plain";
} })
response = Optional(<NSHTTPURLResponse: 0x17002c8a0> { URL: pttp://10.0.1.147/setpins?D2=1023 } { status code: 200, headers {
"Access-Control-Allow-Origin" = "*";
Connection = close;
"Content-Length" = 7;
"Content-Type" = "text/plain";
You don't actually need if-else in that case. Here is your completely rewritten code - try to run it and see if duplication issue goes away.
let switchOn = mySwitch.isOn
switchState.text = switchOn ? "OFF" : "ON"
let endpoint = "http://10.0.1.147/setpins"
let urlString = endpoint + "?D2=" + (switchOn ? "0" : "1023")
mySwitch.setOn(!switchOn, animated:true)
var request = URLRequest(url: URL(string: urlString)!)
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
print("error=\(error)")
return
}
// You can print out response object
print("response = \(response)")
}
task.resume()
Try moving the task.resume out of the if..else block along with other declarations.
//declare other variables here
if .... {
...
...
...
} else {
...
...
}
let task = URLSession.shared.dataTask(with: request) {
(data: Data?, response: URLResponse?, error: Error?) in
if error != nil{
print("error=\(error)")
return
}
// You can print out response object
print("response = \(response)")
}
task.resume()
Related
I need some help with my school project.
Why is print(segueShouldOccur) printed before doAPI().
When I actually call doApi() before the print(seagueShouldOccur).
I'm talking about the method: shouldPerformSegue.
The Rest Api does work (already tested).
class ViewController: UIViewController {
var loginArr = [String]()
#IBOutlet weak var _output: UILabel!
#IBOutlet weak var _username: UITextField!
#IBOutlet weak var _password: UITextField!
#IBAction func doLogin(_ sender: Any) {
loginArr.removeAll()
let username = _username.text;
let password = _password.text;
loginArr.append(username!);
loginArr.append(password!);
self._output.text = username;
}
func doApi() -> Bool{
let headers = [
"cache-control": "no-cache",
"postman-token": "6f8a-12c6-87a1-ac0f25d6385a"
]
let url = "https://projects2018.sz-ybbs.ac.at/~szmed/indyapp/indyapi.php?func=0&user=" + _username.text! + "&pass=" + _password.text!
let request = NSMutableURLRequest(url: NSURL(string: url)! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
var check = false;
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if error == nil && data != nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String:AnyObject]
//do your stuff
print(json);
check = true;
} catch {
}
}
else if error != nil
{
}
}).resume()
return check;
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let DashboardC = segue.destination as! DashboardController
DashboardC.receivedStringArr = loginArr
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "performSegueLogin" { // you define it in the storyboard (click on the segue, then Attributes' inspector > Identifier
var segueShouldOccur = doApi()
if (!segueShouldOccur){
print("1 - false");
print(segueShouldOccur);
return false;
}else{
print("2 - true");
print(segueShouldOccur);
return true;
}
}
return false;
}
}
session.dataTask is asynchronous. If you want to know when your api call has completed, you can use completion handler like this :
func doApi(completion : #escaping (Bool?,Error?) -> ()) {
let headers = [
"cache-control": "no-cache",
"postman-token": "6f8a-12c6-87a1-ac0f25d6385a"
]
let url = "https://projects2018.sz-ybbs.ac.at/~szmed/indyapp/indyapi.php?func=0&user=" + _username.text! + "&pass=" + _password.text!
let request = NSMutableURLRequest(url: NSURL(string: url)! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
var check = false;
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if error == nil && data != nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String:AnyObject]
//do your stuff
print(json);
check = true;
} catch {
}
completion(true,nil)
}
else if error != nil
{
completion(false,error)
}
}).resume()
return check;
}
then you can call your function like this :
doApi { (isSuccess, errorMessage) in
if isSuccess {
// perform your operations
} else {
print(errorMessage?.localizedDescription ?? "Some error occured")
}
}
Let me describe view controller (page design) first. I have added one button and one label. And a function is created by me for the button. Here is the ViewController.swift file:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var btnHitWebService: UIButton!
#IBOutlet weak var lblResult: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnHitWebServiceClick(_ sender: Any) {
if (getTokenWithPostMethod() == true)
{
lblResult.text = "yes, we get token: " + g_token
}
else
{
lblResult.text = "there is an error, please try again later"
}
}
func getTokenWithPostMethod() -> Bool{
var funcResult = false
var request = URLRequest(url: URL(string: "https://apiABCDEFG.com/token")!)
request.httpMethod = "POST"
let postString = "grant_type=password&username=ABCDEF&password=ABCDEF"
request.httpBody = postString.data(using: .utf8)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request)
{
data, response, error in
guard let data = data, error == nil else
{
// check for fundamental networking error
print("error=\(String(describing: error))")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200
{
// check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
}
do
{
if let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject]
{
g_token = (jsonObject["access_token"] as? String)!
print(g_token)
g_token_type = (jsonObject["token_type"] as? String)!
print("\n")
print(g_token_type)
funcResult = true ;
}
}
catch let error as NSError
{
print(error)
}
}
task.resume()
return funcResult
}
}
Here is my problem: When I click button, it gives me a token in output screen in Xcode (print) but getTokenWithPostMethod() method returns false. Why does it return false? During the debug process, it jumps from let task = to task.resume(). Thanks! (by the way I can't share correct link, username and password. I hope these 3 are not necessary to fix this problem)
You are making an API call. It takes time to return the value. Here return statement will not wait for response. you should use either closure or delegate. You can try the given code:
func getTokenWithPostMethod(handler: #escaping (_ funcResult: Bool) -> Void){
var request = URLRequest(url: URL(string: "https://apiABCDEFG.com/token")!)
request.httpMethod = "POST"
let postString = "grant_type=password&username=ABCDEF&password=ABCDEF"
request.httpBody = postString.data(using: .utf8)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request)
{
data, response, error in
var funcResult = false
if error == nil, let data = data {
do
{
if let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject]
{
g_token = (jsonObject["access_token"] as? String)!
print(g_token)
g_token_type = (jsonObject["token_type"] as? String)!
print("\n")
print(g_token_type)
funcResult = true
}
}
catch let error as NSError
{
print(error)
}
}
// edited
DispatchQueue.main.async {
handler(funcResult)
}
}
task.resume()
}
This will return after the response comes. Replace the calling of that method with :
#IBAction func btnHitWebServiceClick(_ sender: Any) {
getTokenWithPostMethod { (flag) in
if flag {
lblResult.text = "yes, we get token: " + g_token
} else {
lblResult.text = "there is an error, please try again later"
}
}
}
For more details please visit
It is returning false because, you have set var funcResult = false in starting, so before URLSession request to server and return some data in response only, your function returns the funcResult value.
you can add below code inside URLSession task body, immediately after try catch, instead of adding it inside btnHitWebServiceClick :
if (getTokenWithPostMethod() == true)
{
lblResult.text = "yes, we get token: " + g_token
}
else
{
lblResult.text = "there is an error, please try again later"
}
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
}
I'm login through an API using a POST request with URLRequest and URLSession. Everything seems to work well since I receive the correct response from the API.
Then I want to send a GET request to the same API but it seems that I'm not connected anymore.
For what I saw, the API is sending a Cookie in its Header in that form :
Cookie:PHPSESSID=abc123
I guess the problem is coming from that cookie wich is not send when I do a GET Request.
Here is my code.
ViewController :
#IBAction func Connection(_ sender: AnyObject) {
let loginFunc = Login()
loginFunc.login(username: username.text!, password: password.text!) { jsonString in
let response = jsonString
print(response)
}
let get = GetRequest()
get.get(req: "patient/info") { jsonString in
let response = jsonString
print(response)
}
}
Login.swift :
class Login {
func login(username: String, password: String, completion: #escaping (String) -> ()) {
var request = URLRequest(url: URL(string: "http://myurl/web/app_dev.php/login_check")!)
request.httpMethod = "POST"
let config = URLSessionConfiguration.default
let postString = "_username=" + username + "&_password=" + password
request.httpBody = postString.data(using: .utf8)
var responseString = ""
let mysession = URLSession.init(configuration: config)
let task = mysession.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
responseString = String(data: data, encoding: .utf8)!
let httpResponse = response as! HTTPURLResponse
let field = httpResponse.allHeaderFields["Cookie"]
print(field)
print(type(of: response))
completion(responseString)
}
task.resume()
}
}
GetRequest.swift :
class GetRequest {
func get(req: String, completion: #escaping (String) -> ()) {
var request = URLRequest(url: URL(string: "http://myurl/web/app_dev.php/" + req)!)
request.httpMethod = "GET"
var responseString = ""
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
responseString = String(data: data, encoding: .utf8)!
print(responseString)
completion(responseString)
}
task.resume()
}
}
Hey for sending cookies along with your ajax request please use. You need to use withCredentials to true.
$.ajaxSetup({
dataType: 'json',
contentType: 'application/json',
async: true,
cache: false,
processData: false,
crossDomain: true,
xhrFields: {
withCredentials: true
}
});
I create functionLibrary.swift class to call some functions in some different ViewControllers. Problem is when I execute the app, the println function in the ViewController.swift works first in spite of it is after the some other lines that I called from functionLibrary.
Here is the logIn function from ViewController. (funcLib is a value that type of functionLibrary)
#IBAction func logIn(sender: AnyObject) {
var bodyData = ["op":"users","op2":"login","id":"","id2":"","id3":"","users_emailAddress":"asdasd#gmail.com","users_password":"asdasd"]
var requestData = funcLib.JSONStringify(bodyData) // This turns the data to string
var responseData = funcLib.HTTPPostRequest(requestData) // This is the http post request. Returns a bool and a Dictionary
println(responseData)
Here is the output
(false, [:]) //responseData
response = <NSHTTPURLResponse: 0x7aad7290> { URL: http://asdasd.asdasd.com/services/index.php } { status code: 200, headers {
Connection = "keep-alive";
"Content-Length" = 57;
"Content-Type" = "application/json";
Date = "Sun, 26 Jul 2015 21:09:39 GMT";
Server = asdasd;
"X-Powered-By" = "PHP/5.4.43";
} }// The println in HTTPPostRequest
{
auth = *********;
"user_id" = ***;
} // The println in HTTPPostRequest
The code is working fine. There are no runtime errors. But like I said println is executed first. How can I execute it after request and response Data get the data from funcLib functions?
Thank you.
Looks like funcLib.HTTPPostRequest schedules an async closure and returns immediately, without waiting for the HTTP response. The async approach is fine, but of course you cannot expect to find in responseData any data returned from the HTTP server, and of course the println in logIn will be executed immediately, while the println of the HTTP response will happen later, when the actual response will be received by the closure.
I solved the problem with using dispach_async() method.
Here is the new HTTPPostRequest() function:
func HTTPPostRequest(link:String, bodyData: AnyObject, completion:((data: NSData?) -> Void)) {
println("HTTPPOST")
if let requestURL = NSURL(string: link){
let request = NSMutableURLRequest(URL: requestURL)
request.HTTPMethod = "POST"
let postString = JSONStringify(bodyData)
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
NSURLSession.sharedSession().dataTaskWithRequest(request){ (data, response, error) in
completion(data: NSData(data: data))
if let error = error {
println("error:\(error)")
return
}
}.resume()
}
}
And here is how to call the HTTPPostRequest():
#IBAction func logIn(sender: AnyObject) {
var bodyData: AnyObject = ["op":"users","op2":"login","id":"","id2":"","id3":"","users_emailAddress":"asdasdasd","users_password":"asdasd"] as AnyObject
let response: () = funcLib.HTTPPostRequest("http://asdasd.asdasdasd.com/services/index.php", bodyData: bodyData){data in
dispatch_async(dispatch_get_main_queue()){
if let data = data {
let responseData = NSData(data: data)
let responseDic = NSJSONSerialization.JSONObjectWithData(data , options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary
println("\(responseDic)")
}
}
}
}
}