Why does post request work much faster from the second call? - swift

I launch the app. Press the button which call this function, wait 2-3 seconds and than get the JSON. Then I press it again, wait 0-1 and then I get the JSON. Can you explain me the reason why it's happening and how can I avoid it?
public func serverUserRegister(userPhone: String?, userEmail: String?, completionHandler: #escaping ((String) -> ())) {
if let phone = userPhone {
if let email = userEmail {
let url = URL(string: "https://example.com/service.php")!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = "phone=\(phone)&email=\(email)"
request.httpBody = paramString.data(using: .utf8)
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
guard let _: Data = data, let _: URLResponse = response, error == nil else {
return
}
if let json = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary {
if let jsonStatus = json?["status"] as? String {
completionHandler(jsonStatus)
}
}
}
task.resume()
}
}
}

Related

Swift, how can I return the data from HTTP request?

I have found learning swift to be more or less unbearable to do anything, something that would be done in a single line in Python becomes a whole task in swift.
I am trying to return the data from a http request and cannot find a single source that explains how. The only things I can find prints the data instead of returning it, either as a dictionary (from using JSONSerialization) or simply as a string.
let url = URL(string: "url")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print("error: \(error)")
} else {
if let response = response as? HTTPURLResponse {
print("statusCode: \(response.statusCode)")
}
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("data: \(dataString)")
}
}
}
task.resume()
func makePostRequest(){
let urlPath: String = "http://www.swiftdeveloperblog.com/http-post-example- script/"
var url: NSURL = NSURL(string: urlPath)!
var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
var stringPost="firstName=James&lastName=Bond" // Key and Value
let data = stringPost.dataUsingEncoding(NSUTF8StringEncoding)
request.timeoutInterval = 60
request.HTTPBody=data
request.HTTPShouldHandleCookies=false
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary
if (jsonResult != nil) {
// Success
println(jsonResult)
let message = jsonResult["Message"] as! NSString
println(message)
}else {
// Failed
println("Failed")
}
})
}

I am doing a post request where I want to type in a question and with the post request get the most common answer

I have done my Post-request but I am unsure about how to make it possible to send a full question and to get the most common answers back to my app.
I am in such a big need of this code in my program so would love to get some examples on how to make it work
Have tried to right the question into the parameters with a "+" instead of space which resulted into nothing.
#IBAction func GetAnswer(_ sender: Any) {
let myUrl = URL(string: "http://www.google.com/search?q=");
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"
let postString = questionAsked;
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(String(describing: error))")
return
}
print("response = \(String(describing: response))")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
let answer = parseJSON[" Answer "] as? String
self.AnswerView.text = ("Anwer: \(String(describing: answer))")
}
} catch {
print(error)
}
}
task.resume()
}
You do not use google.com/search, please check the api documentation
Paste following in Playground, should give a good start
struct Constants {
static let apiKey = "YOUR_API_KEY"
static let bundleId = "YOUR_IOS_APP_BUNDLE_ID"
static let searchEngineId = "YOUR_SEARCH_ENGINE_ID"
}
func googleSearch(term: String, callback:#escaping ([(title: String, url: String)]?) -> Void) {
let urlString = String(format: "https://www.googleapis.com/customsearch/v1?q=%#&cx=%#&key=%#", term, Constants.searchEngineId, Constants.apiKey)
let encodedUrl = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
guard let url = URL(string: encodedUrl ?? urlString) else {
print("invalid url \(urlString)")
return
}
let request = NSMutableURLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10)
request.httpMethod = "GET"
request.setValue(Constants.bundleId, forHTTPHeaderField: "X-Ios-Bundle-Identifier")
let session = URLSession.shared
let datatask = session.dataTask(with: request as URLRequest) { (data, response, error) in
guard
error == nil,
let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String : Any]
else {
// error handing here
callback(nil)
return
}
guard let items = json["items"] as? [[String : Any]], items.count > 0 else {
print("no results")
return
}
callback(items.map { ($0["title"] as! String, $0["formattedUrl"] as! String) })
}
datatask.resume()
}
Usage
googleSearch(term: "George Bush") { results in
print(results ?? [])
}
Create a new search engine using following url
https://cse.google.com/cse/create/new
If you would like search entire web, use following steps
edit your engine using https://cse.google.com/cse/setup/basic?cx=SEARCH_ENGINE_ID
remove any pages listed under Sites to search
turn on Search the entire web

How to wait for a download task to finish in swift 3

I am trying to build a user regeistration form, which should check if the user already exists. So I am sending a php request to my my mysql server. If the return value is empty, the user does not exists yet.
Unfortunatley I am really stuck with waiting for this check to finish. I tried several solutions I found googleing but none of them worked. My current code uses semaphores and will crash with "fatal error: unexpectedly found nil while unwrapping an Optional value", so the semaphore is not waiting until the task is finished as I would expect it.
Any hints, would be greatly appreciated. Thanks guys.
private func isUniqueEmail(email: String) -> Bool {
var result: Bool?
let semaphore = DispatchSemaphore(value: 1)
let requestURL = URL(string: "http://localhost/firstpostget/functions/get.php")
var request = URLRequest(url: requestURL!)
request.httpMethod = "POST"
let postParameters = "email=" + email
request.httpBody = postParameters.data(using: .utf8)
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) {(data, response, error) in
var myJson: AnyObject
do{
myJson = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if myJson.count == 0{
result = true
semaphore.signal()
} else{
result = false
semaphore.signal()
}
} catch{
//TODO
print(error)
}
}
task.resume()
semaphore.wait(timeout: .distantFuture)
return result!
}
Your task is async and you are force unwrapping nil value so this is the reason it crashes.
You have to change your function implementation to also be async, for example using closures:
private func isUniqueEmail(email: String, completion: ((Bool) -> (Void))?) {
let requestURL = URL(string: "http://localhost/firstpostget/functions/get.php")
var request = URLRequest(url: requestURL!)
request.httpMethod = "POST"
let postParameters = "email=" + email
request.httpBody = postParameters.data(using: .utf8)
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) {(data, response, error) in
var myJson: AnyObject
do{
myJson = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if myJson.count == 0 {
completion?(true)
} else{
completion?(false)
}
} catch{
//TODO
print(error)
}
}
task.resume()
}
Now you can use this function in this way:
isUniqueEmail(email: "aaa#bbbb.com") { result in
if result {
print("email unique")
} else {
print("email not unique")
}
}
I think you should rethink the pattern you're using to get the data out of your request, you should consider using a custom handler/callback method that you pass along with the email you're trying to check. See below for an example:
private func isUniqueEmail(email: String, handler: ((_ result: Bool) -> Void)?) -> Void {
let requestURL = URL(string: "http://localhost/firstpostget/functions/get.php")
var request = URLRequest(url: requestURL!)
request.httpMethod = "POST"
let postParameters = "email=" + email
request.httpBody = postParameters.data(using: .utf8)
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) {(data, response, error) in
var myJson: AnyObject
var result: Bool = false
do{
myJson = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if myJson.count == 0 {
result = true
}
guard handler != nil else {
return
}
handler!(result)
} catch{
//TODO
print(error)
}
}
task.resume()
}
Run:
isUniqueEmail(email: "test#test.com", handler: { result in
print(result) // true || false
})
If you really want to go down the "wait" route then take a took at DispatchGroup's
https://developer.apple.com/documentation/dispatch/dispatchgroup
try using this:
ApihelperClass
static let sharedInstance = ApihelperClass()
typealias CompletionHandler = (_ success:Bool, _ error:Bool, _ result:NSDictionary) -> Void
typealias ErrorHandler = (_ success: Bool, _ error:Bool) -> Void
func callPostRequest(_ urlPath: String, params:[String: AnyObject], completionHandler: #escaping CompletionHandler, errorHandler:#escaping ErrorHandler ){
print("urlPath:==> \(urlPath) ")
let session = Foundation.URLSession.shared
let url = URL(string: urlPath)
var request = URLRequest(url : url!)
request.httpMethod = "POST"
do {
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
session.dataTask(with: request, completionHandler: { data, response, error in
OperationQueue.main.addOperation {
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
errorHandler(false, true)
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)")
}
let responseString = String(data: data!, encoding: String.Encoding.utf8)
print("responseString = \(responseString!)")
if let responsedata = responseString!.data(using: String.Encoding.utf8)! as? Data{
do {
let jsonResult:NSDictionary = try JSONSerialization.jsonObject(with: responsedata, options: []) as! NSDictionary
print("Get The Result \(jsonResult)")
//parse your jsonResult as per your requirements
if error != nil {
print("error=\(error)")
completionHandler(false, true, jsonResult)//
}
if let str = jsonResult["success"] as? NSNull {
print("error=\(str)")
completionHandler(false, true, jsonResult)
}
else {
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
// print("Response string : \(responseString)")
completionHandler(true, false, jsonResult)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
}) .resume()
}catch {
print("Error ->Catch")
}
}
Add to your viewcontroller
func isUniqueEmail(email: String){
ApihelperClass.sharedInstance.callPostRequest("http://localhost/firstpostget/functions/get.php", params: ["email":email as AnyObject], completionHandler: { (success, error, result) in
//success 200
}) { (success, error) in
//error
}
}
you can use URlSession like :
func isUniqueEmail(email: String,completion: #escaping (Bool) -> ()) {
var request = URLRequest(url: URL(string: "http://localhost/firstpostget/functions/get.php")!)
request.httpMethod = "POST"
let postString = "email=\(email)"
request.httpBody = postString.data(using: .utf8)
// loading to wait request
UIApplication.shared.isNetworkActivityIndicatorVisible = true
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// we get request request
UIApplication.shared.isNetworkActivityIndicatorVisible = false
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(String(describing: error))")
completion(false)
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))")
completion(false)
}else{
completion(true)
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(String(describing: responseString))")
}
task.resume()
}
and used in code Like
self.isUniqueEmail(email: "your Email") { (isExit) in
if isExit {
}else{
}
}
Ok, I just found a solution. My semaphore approach actually worked as well as dispatchgroups. The task just needed to be URLSession.shared.dataTask
Still thank's a lot for all the answers.

swift URLRequest doesn't send parameters

In the code below
let bodyData = "?sub=\(id)&name=User&email=test#test.com"
let url = NSURL(string: "https://httpbin.org/get");
let request:NSMutableURLRequest = NSMutableURLRequest(url:url as! URL)
request.httpMethod = "GET"
request.httpBody = bodyData.data(using: String.Encoding.utf8);
NSURLConnection.sendAsynchronousRequest(request as URLRequest, queue: OperationQueue.main)
{
(response, data, error) in
if let HTTPResponse = response as? HTTPURLResponse {
let statusCode = HTTPResponse.statusCode
if statusCode == 200 {
// Yes, Do something.
let json = try? JSONSerialization.jsonObject(with: data!, options: [])
if let dictionary = json as? [String: Any] {
let args = dictionary["args"]
NSLog("args:\(args)")
for (key, value) in dictionary{
NSLog("key:\(key) value:\(value)")
}
}
}
}
}
id is a string passed in to the function.
It has valid data returned, but the test site/url also returns in json format any parameters you send it. But this snippet of code seems to not be sending the query parameters defined in bodyData and I can't figure out why.
If a GET request is used the parameters are appended to the URL and an explicit URLRequest is not needed at all.
This code is native Swift 3 and uses contemporary API:
let id = 12
let bodyData = "?sub=\(id)&name=User&email=test#test.com"
let url = URL(string: "https://httpbin.org/get" + bodyData)!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String:Any],
let args = json["args"] as? [String:Any] {
print("args:\(args)")
for (key, value) in args{
print("key:\(key) value:\(value)")
}
}
} catch {
print(error)
}
}.resume()

How can i convert String to Dictionary to access nested array and dictionary, i want to access stateId and stateName to add in to my table

i
func callAddWithPOST(Name mname:String, PhoneNo mphone:String, Email memail:String, Comment mcomments:String){
let login = ["countryId":"1"]
print("Your Result is : = \(login)")
let url = NSURL(string: "http://photokeeper.mgtcloud.co.uk/commonwebservice.asmx/getStateList")!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
do {
let auth = try NSJSONSerialization.dataWithJSONObject(login, options: .PrettyPrinted)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
request.HTTPBody = auth
let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
let result = self.convertStringToDictionary(responseString!)
let keys = Array(result!.values)
print(keys[0])
print("Done.")
})
task.resume()
} catch {
print("Error")
}
}
output
{"result":[{"stateId":3871,"stateName":"Aberdeenshire"},{"stateId":3872,"stateName":"Anglesey/Sir Fon"},{"stateId":3873,"stateName":"Angus"},{"stateId":3874,"stateName":"Antrim"},{"stateId":3875,"stateName":"Argyll And Bute"},{"stateId":3876,"stateName":"Armagh"},{"stateId":3877,"stateName":"Ayrshire"},{"stateId":3878,"stateName":"Bedfordshire"},{"stateId":3879,"stateName":"Berkshire"},{"stateId":3880,"stateName":"Blaenau Gwent/Blaenau Gwent"},{"stateId":3881,"stateName":"Bristol"},{"stateId":3882,"stateName":"Buckinghamshire"},{"stateId":3883,"stateName":"Caerphilly/Caerffili"},{"stateId":3884,"stateName":"Cambridgeshire"},{"stateId":3885,"stateName":"Cardiff/Caerdydd"},{"stateId":3886,"stateName":"Cardiganshire/Ceredigion"},{"stateId":3888,"stateName":"Carmarthenshire/Sir Gaerfyrddin"},{"stateId":3890,"stateName":"Cheshire"},{"stateId":3891,"stateName":"Clackmannanshire"},{"stateId":3893,"stateName":"Conwy/Conwy"},{"stateId":3895,"stateName":"County Durham"},{"stateId":3896,"stateName":"Cumbria"},{"stateId":3897,"stateName":"Denbighshire/Sir Ddinbych"},{"stateId":3898,"stateName":"Derbyshire"},{"stateId":3899,"stateName":"Devon"},{"stateId":3901,"stateName":"Dorset"},{"stateId":3902,"stateName":"Down"},{"stateId":3904,"stateName":"Dumfries And Galloway"},{"stateId":3905,"stateName":"Dunbartonshire"},{"stateId":3906,"stateName":"Dundee"},{"stateId":3907,"stateName":"Durham/North Yorkshire"},{"stateId":3908,"stateName":"East Lothian"},{"stateId":3909,"stateName":"East Sussex"},{"stateId":3910,"stateName":"East Yorkshire"},{"stateId":3911,"stateName":"Edinburgh"},{"stateId":3912,"stateName":"Essex"},{"stateId":3913,"stateName":"Fermanagh"},{"stateId":3914,"stateName":"Fife"},{"stateId":3915,"stateName":"Flintshire/Sir Fflint"},{"stateId":3917,"stateName":"Glamorgan/Morgannwg"},{"stateId":3918,"stateName":"Glasgow"},{"stateId":3919,"stateName":"Gloucestershire"},{"stateId":3920,"stateName":"Gwynedd/Gwynedd"},{"stateId":3921,"stateName":"Hampshire"},{"stateId":3922,"stateName":"Herefordshire"},{"stateId":3923,"stateName":"Hertfordshire"},{"stateId":3924,"stateName":"Highland"},{"stateId":3925,"stateName":"Kent"},{"stateId":3929,"stateName":"Lanarkshire"},{"stateId":3930,"stateName":"Lancashire"},{"stateId":3932,"stateName":"Leicestershire"},{"stateId":3935,"stateName":"Lincolnshire"},{"stateId":3936,"stateName":"London"},{"stateId":3937,"stateName":"Londonderry"},{"stateId":3940,"stateName":"Manchester"},{"stateId":3943,"stateName":"Merthyr Tydfil/Merthyr Tydfil"},{"stateId":3944,"stateName":"Midlothian"},{"stateId":3946,"stateName":"Monmouthshire/Sir Fynwy"},{"stateId":3947,"stateName":"Moray"},{"stateId":3948,"stateName":"Neath Port Talbot"},{"stateId":3949,"stateName":"Newport"},{"stateId":3950,"stateName":"Norfolk"},{"stateId":3951,"stateName":"Northamptonshire"},{"stateId":3952,"stateName":"Northumberland"},{"stateId":3953,"stateName":"Nottinghamshire"},{"stateId":3955,"stateName":"Orkney"},{"stateId":3956,"stateName":"Oxfordshire"},{"stateId":3957,"stateName":"Pembrokeshire/Sir Benfro"},{"stateId":3958,"stateName":"Perth And Kinross"},{"stateId":3959,"stateName":"Powys/Powys"},{"stateId":3960,"stateName":"Renfrewshire"},{"stateId":3962,"stateName":"Rutland"},{"stateId":3963,"stateName":"Scottish Borders"},{"stateId":3964,"stateName":"Shetland Isles"},{"stateId":3965,"stateName":"Shropshire"},{"stateId":3967,"stateName":"Somerset"},{"stateId":3968,"stateName":"South Yorkshire"},{"stateId":3969,"stateName":"Staffordshire"},{"stateId":3970,"stateName":"Stirling"},{"stateId":3971,"stateName":"Suffolk"},{"stateId":3972,"stateName":"Surrey"},{"stateId":3973,"stateName":"Swansea"},{"stateId":3975,"stateName":"Torfaen"},{"stateId":3976,"stateName":"Tyrone"},{"stateId":3977,"stateName":"Warwickshire"},{"stateId":3979,"stateName":"West Lothian"},{"stateId":3980,"stateName":"West Midlands"},{"stateId":3981,"stateName":"West Sussex"},{"stateId":3982,"stateName":"West Yorkshire"},{"stateId":3983,"stateName":"Western Isles"},{"stateId":3987,"stateName":"Wiltshire"},{"stateId":3988,"stateName":"Worcestershire"},{"stateId":3989,"stateName":"Wrexham"}],"status":"success"}
Done.
Instead of converting data to String try to convert it Dictionary direct
let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
let response = try NSJSONSerialization.JSONObjectWithData(data!, options:.MutableContainers) as! [String: AnyObject]
let status = response["status"] as! String
if status == "success" {
let array = response["result"] as! [[String: AnyObject]]
print(array)
}
})
Note: You have already written your code inside do block thats why i have not declare that block for try, you just need to replace your task block with my one.
Edit:
func callAddWithPOST(Name mname:String, PhoneNo mphone:String, Email memail:String, Comment mcomments:String){
let login = ["countryId":"1"]
print("Your Result is : = \(login)")
let url = NSURL(string: "http://photokeeper.mgtcloud.co.uk/commonwebservice.asmx/getStateList")!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
do {
let auth = try NSJSONSerialization.dataWithJSONObject(login, options: .PrettyPrinted)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
request.HTTPBody = auth
let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
let response = try NSJSONSerialization.JSONObjectWithData(data!, options:.MutableContainers) as! [String: AnyObject]
let status = response["status"] as! String
if status == "success" {
let array = response["result"] as! [[String: AnyObject]]
print(array)
}
})
task.resume()
} catch let error as NSError {
print(error.localizedDescription)
}
}