Swift add params to request - swift

I'm trying to add an email and a password to a NSURLSession request like this:
#IBAction func btnLogin(sender: AnyObject) {
let email = txtEmail.text
let password = txtPassword.text
let data = ["email": email!, "password": password!] as Dictionary<String, String>
let session = NSURLSession.sharedSession()
let url = NSURL(string: "http://temp.com/api/v1/login")!
let request = NSMutableURLRequest(URL: url)
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(data, options: [])
}
catch {
print("error!")
}
request.HTTPMethod = "POST"
let task = session.dataTaskWithRequest(request) {
(data, response, error) in
guard let response = response as? NSHTTPURLResponse else {print(error); return}
if response.statusCode == 200 {
print("ingelogd!")
}
self.errorLogin()
}
task.resume()
}
error:
2016-07-03 17:54:04.890 EmployeeDirectory2[4693:133721] *** Assertion failure in -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.60.7/Keyboard/UIKeyboardTaskQueue.m:386
2016-07-03 17:54:04.894 EmployeeDirectory2[4693:133721] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'
But when I try this it crashes. How can I add parameters appropriately?

The crash is probably coming from the call to self.errorLogin() call, which tries to do some UI task outside the main thread. You should be able to fix it like this:
let task = session.dataTaskWithRequest(request) {
(data, response, error) in
guard let response = response as? NSHTTPURLResponse else {print(error); return}
if response.statusCode == 200 {
print("ingelogd!")
}
// Add this:
dispatch_async(dispatch_get_main_queue()) {
self.errorLogin()
}
}

Related

Stripe: Registered webhook but still getting "Not a Valid URL" error

I seem to be running into an error each time I try to make a payment transaction where Stripe declines the operation by saying "Not a valid URL". The client-side is in Swift, while the server is in Python, deployed on Heroku.
A suggestion I saw on another post was to register my server webhook with my Stripe account, which I did, but it doesn't seem to solve the problem.
For reference, here is my createPaymentIntent function:
func createPaymentIntent(dict: [String:Any]) {
let url = self.baseURL.appendingPathComponent("create-payment-intent")
let params = dict
let jsondata = try? JSONSerialization.data(withJSONObject: params)
var clientSecretOut = ""
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, completionHandler: { [weak self] (data, response, error) in
guard let response = response as? HTTPURLResponse,
response.statusCode == 200,
let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any],
let clientSecret = json["clientSecret"] as? String else {
let message = error?.localizedDescription ?? "Failed to decode response from server."
print("Error: ", message)
return
}
clientSecretOut = clientSecret
print("client out inside: ", clientSecretOut)
self?.clientSecretFinal = clientSecret
})
task.resume()
}
And here is the place where the error seems to be called– my didCreatePaymentResultFunction:
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: #escaping STPPaymentStatusBlock) {
let paymentIntentParams = STPPaymentIntentParams(clientSecret: self.clientSecretFinal)
paymentIntentParams.configure(with: paymentResult)
paymentIntentParams.returnURL = "meURLStripeTest://"
STPPaymentHandler.shared().confirmPayment(withParams: paymentIntentParams, authenticationContext: paymentContext) { status, paymentIntent, error in
switch status {
case .succeeded:
print("success")
completion(.success, nil)
case .failed:
print("cant", error)
completion(.error, error)
case .canceled:
completion(.userCancellation, nil)
#unknown default:
completion(.error, nil)
}
}
}
Thanks for your help!
The problem is in this line:
paymentIntentParams.returnURL = "meURLStripeTest://"
The returnURL has to be a valid URL or application URI scheme, in your case it's neither. See here on how to create an application URI scheme for your app.

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

How to show message when Task fails due to Timeout

I need a clean way for this example to print("ERROR") when the task ends due to the timeout.
func getUserDataService() -> Bool{
var getDataStatus = false
//Create the url with NSURL
let url = URL(string: "http://someurl")! //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 = "GET"
request.timeoutInterval = 15
//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] {
if let httpResponse = response as? HTTPURLResponse {
if httpResponse.statusCode == 200 {
getDataStatus = true
} else {
}
}
}
} catch let error {
print(error)
}
})
task.resume()
return getDataStatus
}
It prints the following when it fails, but I'm not sure where to control this.
2019-05-27 13:53:58.501322-0400 AppName[60195:841789] Task <AB410EF3-5520-44AD-A458-DD75D1F6AD09>.<3> finished with error - code: -1001
2019-05-27 13:53:58.505525-0400 AppName[60195:842001] Task <AB410EF3-5520-44AD-A458-DD75D1F6AD09>.<3> HTTP load failed (error code: -999 [1:89])
Please read the documentation URL Loading System Error Codes. The timeout error is -1001 aka NSURLErrorTimedOut
The error is returned in the completion handler of the data task. Handle it!
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
if let nserror = error as NSError?,
nserror.code == NSURLErrorTimedOut {
// do something
return
}
Apart from that you cannot return a Bool value from that method, you have to add a completion handler
func getUserDataService(completion: #escaping (Bool) -> Void) {
...
And call
completion(true)
and delete
return getDataStatus

function with dataTask returning a value

I wan't to check if my url statusCode equals to 200, I created a function returning a Boolean if the statusCode equals to 200, I'm using a dataTask, but I don't know how to return a value:
class func checkUrl(urlString: String) -> Bool{
let urlPath: String = urlString
var url: NSURL = NSURL(string: urlPath)!
var request: NSURLRequest = NSURLRequest(url: url as URL)
var response: URLResponse?
let session = Foundation.URLSession.shared
var task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let data = data{
print("data =\(data)")
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
if httpResponse.statusCode == 200{
return true
} else {
return false
}
}
})
task.resume()
}
The returns in if else are returning an error:
Unexpected non-void return value in void function
in order to return value you should use blocks. Try declaring your function like this:
class func checkUrl(urlString: String, finished: ((isSuccess: Bool)->Void) {
let urlPath: String = urlString
var url: NSURL = NSURL(string: urlPath)!
var request: NSURLRequest = NSURLRequest(url: url as URL)
var response: URLResponse?
let session = Foundation.URLSession.shared
var task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let data = data{
print("data =\(data)")
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
if httpResponse.statusCode == 200{
finished(isSuccess: true)
} else {
finished(isSuccess: false)
}
}
})
task.resume()
}
And then call it like this:
checkUrl("http://myBestURL.com", finished { isSuccess in
// Handle logic after return here
})
Hope that this will help.
Consider semaphore if you want to keep your original return pattern.
func checkUrl(urlString: String) -> Bool {
if let url = URL(string: fileUrl) {
var result: Bool!
let semaphore = DispatchSemaphore(value: 0) //1. create a counting semaphore
let session = URLSession.shared
session.dataTask(with: url, completionHandler: { (data, response, error) in
result = true //or false in case
semaphore.signal() //3. count it up
}).resume()
semaphore.wait() //2. wait for finished counting
return result
}
return false
}
Swift4, work in my case
Try to add guard let data = data else { return } in dataTask like:
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
print("get some data")
}.resume()
You're returning a value from a Void function that is the completionHandler closure of dataTask(_:, _:)
Regarding your code, there is something wrong: you can't return that value because it's executed on a different thread, it's an asynchronous operation. Please take a look at this thread: Returning data from async call in Swift function

How to use NSURLResponse as segue trigger swift

I'm doing this in my project.
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.12345.biz/BpjsHost/NonTransactionManager/ValidateAccount")!)
request.setValue(apikey, forHTTPHeaderField: "APIKey")
request.HTTPMethod = "POST"
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response, data, error) in
let postString = ("msisdn=\(userMsisdn!)")
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else {
print("error=\(error)")
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 {
print("error = \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString!)")
}
task.resume()
}
and this is the response:
responseString = {"id":"9082","groupID":"13","status":"PROCESSED","msisdn":"085959981892","name":"Prio","cf_partner_id":null}
How to use the response status which is "PROCESSED" as trigger to perform segue to another view controller.
On the closure that handles the response from your request, you can do something along the lines of this:
//- after your guard error = nil ...
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as! NSDictionary {
if let status = json["status"] as! String {
if (status == "PROCESSED"){
//- this is still in a separate thread
//- lets go back to the main thread!
dispatch_async(dispatch_get_main_queue(), {
//- this happens in the main thread
performSegueWithIdentifier("blahblah", sender: self)
});
}
}
}
catch {
//handle error
}
This will serialize the response into a dictionary, so we can then unwrap its "status" member and invoke the segue in the main thread. Its important to remember to execute the segue in the main thread as your completion handler will perform its task in a separate thread.
Good luck!