How to execute two asynchronous functions sequentially - ios

func getTopicIdFromMYSQL(){
let myUrl = NSURL(string: "xxxx")
let request = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"
let email:String = "xxx#gmail.com"
let postString = "email=\(email)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
if(error != nil){
print("Get all topic")
print("error=\(error)")
return
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
if let parseJSON = json
{
let resultValue = parseJSON["status"] as? String
print("Get all topic")
favouriteTopic = parseJSON["getResult"]! as! [AnyObject]
print("return topic:\(favouriteTopic)")
dispatch_async(dispatch_get_main_queue(), {
if(resultValue == "Success"){
}
else{
let error = UIAlertController(title: "Error", message: "Please check your network configuration!:-(", preferredStyle: .Alert)
let cancel = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
let ok = UIAlertAction(title: "OK", style: .Default, handler: nil)
error.addAction(cancel)
error.addAction(ok)
}
})
}
}catch
{
print(error)
}
}
task.resume()
}
I have already got this code, how ever, I want to run another function after it. What should I do. like:
getTopicIdFromMYSQL()
getCommentFromMYSQL()
print("Finish")
I find the problem is my code does not excute in order, the function getCommentFromMYSQL is almost the same as getTopicIdFromMYSQL, I want to run these three in order what should I do?

Add a completion handler to your asynchronous functions as a parameter:
func getTopicIdFromMYSQL(completion: (AnyObject?, ErrorType?)->())
func getCommentFromMYSQL(completion: (AnyObject?, ErrorType?)->())
Note:
The completion handler must be eventually called when the asynchronous function completes - either with an error or the computed value.
You then call these functions as shown below:
getTopicIdFromMYSQL() { (result1, error) in
if let result1 = result1 {
// process result1
// ...
getCommentFromMYSQL() { (result2, error) in
if let result2 = result2 {
// process result2
// ...
} else {
// handle error
}
}
} else {
// handle error
}
}
You may implement these functions as follows:
func getTopicIdFromMYSQL(completion: (AnyObject?, ErrorType?) {
let myUrl = NSURL(string: "xxxx")
let request = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"
let email:String = "xxx#gmail.com"
let postString = "email=\(email)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
if error != nil {
completion(nil, error)
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
if let parseJSON = json {
let resultValue = parseJSON["status"] as? String
print("Get all topic")
favouriteTopic = parseJSON["getResult"]! as! [AnyObject]
completion(favouriteTopic, nil)
} else {
throw MyError.Error(message: "bogus JSON")
}
} catch let error {
completion(nil, error)
}
}
task.resume()
}

The code is always executed in order. I think your problem is that you are making an asynchronous call in this line:
NSURLSession.sharedSession().dataTaskWithRequest(request)
But, as the code is executed sequentially, the method getCommentFromMYSQL() is called before the asynchronous call finishes. You should call getCommentFromMYSQL() inside this conditional:
if (resultValue == "Success") {
}
Or whenever you want. That will ensure the method getCommentFromMYSQL() to be called after the first method finishes its execution.

This is because, you are making async calls, which run in background thread.
If you want them to be executed in order, call the next method after dispatching to main queue,
dispatch_async(dispatch_get_main_queue(), {
//call here
getCommentFromMYSQL()
if(resultValue == "Success"){
}
else{
let error = UIAlertController(title: "Error", message: "Please check your network configuration!:-(", preferredStyle: .Alert)
let cancel = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
let ok = UIAlertAction(title: "OK", style: .Default, handler: nil)
error.addAction(cancel)
error.addAction(ok)
}
})

Related

Unable to Json response is not working

Swift 3.0 (Xcode 8.3)
I'm trying to make a small program, that send a username to a web data base. I have found out how to send first_name, last_name,dob,owner_mobile,owner_email,
owner_password,choice_for_verification, by using in POST method. I obtain a JSON string earlier, then when I try to parse it, I get the above error on the try NSJSONSerialization line, on the as keyword. What did I do wrong? Thanks for your answers.
#IBAction func Login_Action(_ sender: Any)
{
var responseString : String!
var request = URLRequest(url: URL(string: "http://dev.justpick2go.com/cpanel/api/owner/ownerregistration.php")!)
request.setValue("Application/x-www.ownerregistration.php.com", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let postString = "first_name=\(txtFirstName.text!)&last_name=\(txtLastName.text!)&dob=\(txtDOB.text!)&owner_mobile=\(txtMobileNo.text!)&owner_email=\(txtEmailID.text!)&owner_password=\(txtPassword.text!)" //&choice_for_verification=\(email)" // sending a parameters
print("\(postString)")
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error != nil else { //checking for fundamental error
print("Error is =\(String(describing: error))")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200
{ // checking for http errors
print("statusCode should be 200 , but is\(httpStatus.statusCode)")
print("response is =\(String(describing: response))")
}
responseString = String(data: data, encoding: .utf8)
print("ResponseString=\(responseString!)")
do {
let json : NSDictionary! = try! JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSDictionary
self.parseTheJSonData(JsonData: json)
}
catch
{
print(error)
}
}
task.resume()
}
func parseTheJSonData(JsonData : NSDictionary)
{
var successMessage : String = String()
var sampleCode : Int = Int()
let verificationAlert = UIAlertController()
if ((JsonData.value(forKey: "success") as! Int) == 1)
{
successMessage = "Login is Successful"
sampleCode = JsonData.value(forKey: "success") as! Int
verificationAlert.addAction(UIAlertAction(title: "No", style: .cancel, handler: nil))
verificationAlert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (Relogin) in
let Log = self.storyboard?.instantiateViewController(withIdentifier: "") as! LoginViewController
self.navigationController?.pushViewController(Log, animated: true)
self.present(Log, animated: true, completion: nil)
}))
}
else if ((JsonData.value(forKey: "success") as! Int) == 0)
{
sampleCode = JsonData.value(forKey: "success") as! Int
successMessage = "Please try again"
verificationAlert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
}
verificationAlert.title = successMessage
OperationQueue.main.addOperation
{
self.present(verificationAlert, animated: true, completion: nil)
}
}

IOS Swift - go to another view after getting stats from NURLSESSION

Once the user click the login button, i will call the func LoginClicked and get the status from api:
func LoginClicked(sender: AnyObject)
{
data_request{
(response) -> () in
let arrResponse = response.componentsSeparatedByString("|")
if (arrResponse[2] == "1"){
self.performSegueWithIdentifier("Login", sender: self)
}
else {
let alert = UIAlertController(title: "Login Failed", message: "Invalid Login!", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
}
func data_request(completion : (response:NSString) -> ()){
let txtUI : String = txtUsername!.text!
let txtPWD : String = txtPassword!.text!
let url = NSURL(string: "http://myweb.net/?UI=\(txtUI)&PW=\(txtPWD)")!
let request = NSURLRequest(URL: url)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(request, completionHandler: {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
completion(response : dataString!)
})
task.resume()
}
If success, it will move to another view. Otherwise, show failed alert. it will hit the error BAD_EXECUTION_INSTRUCTION when calling self. in both condition.
After get the hint from #Tj3n, it can do the proper action now.
func data_request(completion : (response:NSString) -> ()){
let txtUI : String = txtUsername!.text!
let txtPWD : String = txtPassword!.text!
let url = NSURL(string: "http://myweb.net/?UI=\(txtUI)&PW=\(txtPWD)")!
let request = NSURLRequest(URL: url)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(request, completionHandler: {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
dispatch_async(dispatch_get_main_queue(), {
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
//let response = dataString?.componentsSeparatedByString("|")
//print(dataString)
//print(response![2])
let arrResponse = dataString!.componentsSeparatedByString("|")
if (arrResponse[2] == "1"){
self.performSegueWithIdentifier("Login", sender: self)
}
else {
let alert = UIAlertController(title: "Login Failed", message: "Invalid Login!", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
})
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
completion(response : dataString!)
})
task.resume()
}

Calling a func in other swift file which works with web-service returns nothing

In swift 2 When I'm communicating with a web-service and when I write these codes in button action it works fine.
let alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: .Alert)
let ok = UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in })
alert.addAction(ok);
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.myaddress.com/web-service/iostest.aspx")!)
request.HTTPMethod = "POST"
var postString = String();
postString = "uid=1";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else {
alert.title="Error"
alert.message = "Connection error"
dispatch_async(dispatch_get_main_queue()){
self.presentViewController(alert, animated: true, completion: nil)
}
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 {
alert.title="Error"
alert.message = "Server error"
dispatch_async(dispatch_get_main_queue()){
self.presentViewController(alert, animated: true, completion: nil)
}
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
alert.title="Info"
alert.message = responseString as? String
dispatch_async(dispatch_get_main_queue()){
self.presentViewController(alert, animated: true, completion: nil)
}
}
task.resume()
As I said this works fine but as I want to do this from different ViewControls as well I have created a swift file which contains a struct and a static func in that struct that returns the the "responseString" so I could alert it in the view control. Something like this:
struct globalClass {
static func sendInfo(url: String, data: String) -> (answer: String, errorCode: Int32) {
var res = String();
var err = Int32();
err = 0;
let request = NSMutableURLRequest(URL: NSURL(string: url)!);
request.HTTPMethod = "POST";
let postString: String = data;
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else {
err = 1;
return;
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 {
err = 2;
return;
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding);
res = (responseString as? String)!;
}
task.resume();
return (res, err);
}
But now when I call this func from my button it shows me an empty alert very fast that it seems like it didn't get anything from web-service and didn't even try too.
I put these in the button action:
#IBAction func btnData(sender: AnyObject) {
let y: String = "uid=1";
let res = globalClass.sendInfo("http://www.myaddress.com/web-service/iostest.aspx", data: y);
let alert = UIAlertController(title: "", message: "", preferredStyle: .Alert);
let OK = UIAlertAction(title: "OK", style: .Default, handler: nil);
alert.addAction(OK);
if (res.errorCode==0) {
alert.title = "Info";
alert.message = res.answer;
} else if (res.errorCode==1) {
alert.title = "Error";
alert.message = "Error connecting to server";
} else {
alert.title = "Error";
alert.message = "Server returned an error";
}
dispatch_async(dispatch_get_main_queue()){
self.presentViewController(alert, animated: true, completion: nil);
};
}
Thanks for the help,
Afshin Mobayen Khiabani
globalClass.sendInfo uses async call - dataTaskWithRequest. The result of the request will be delivered in completion of this method. But you don't wait for that result, instead you try to use sendInfo like a sync function.
To be able to deliver the result from dataTaskWithRequest's completion, put your own completion into sendInfo and invoke this completion (closure) when the result is delivered. An example
struct GlobalClass {
static func sendInfo(url: String, data: String, completion: (answer: String?, errorCode: Int32?) -> Void) {
// you code here which prepares request
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
// you parse the result here
// you deliver the result using closure
completion(string, error)
}
task.resume();
}
}
And an example of usage:
func usage() {
GlobalClass.sendInfo("url", data: "data") { (answer, errorCode) in
// your answer and errorCode here
// handle the result
}
}
static func sendInfo(url: String, data: String, completion: (answer: String, errorCode: Int32) -> ()){
//Your code..
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding);
res = (responseString as? String)!;
completion(answer: res, errorCode: err)
}
task.resume()
}
Then when you call the sendInfo, call like so:
sendInfo(url: "your url", data: "your data") { (result, error) in
//you use your result and error values as u want.
}

Duplicate item in tableview when using searchbar

This is the searchbar delegate that i use
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.characters.count >= 2 {
self.setupinbox(searchText)
}
}
This is the function to get data from server
func setupinbox(q : String) {
arrayOfRels.removeAll(keepCapacity: false)
self.tableView.reloadData()
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: "URL")!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
let data = "devicetoken=\(devicetoken!)&q=\(q)&user_id=\(userid)"
request.HTTPBody = data.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
let res = response as! NSHTTPURLResponse
dispatch_async(dispatch_get_main_queue(), {
if (res.statusCode >= 200 && res.statusCode < 300)
{
do {
let jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers ) as! NSArray
let json = JSON(jsonData)
for (_, subJSON): (String, JSON) in json[0]["interests"] {
let title = subJSON["title"].string
let eID = subJSON["ID"].string
let count = subJSON["count"].string
let rel1 = InboxInterests(title: title!,eventID : NSInteger(eID!)!, count: count!)
self.arrayOfRels.append(rel1)
}
} catch let error as NSError {
print(error)
}
self.tableView.reloadData()
} else {
let alert = UIAlertController(title: "Error", message: "Connection Failed", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (action: UIAlertAction) in
}))
self.presentViewController(alert, animated: true, completion: nil)
}
}
)}
})
task.resume()
}
When i type searchbar slowly, everything is ok, i see results correctly.
But when i type 2 letters very quickly, i get duplicate item.
I also get duplicate items when i remove letters quickly
Tried something like delaying the textDidChange but it didnt work.
Any idea what causes this and how can i fix?
You should put an extra line of code:
do {
arrayOfRels.removeAll(keepCapacity: false)
........

Getting nil value when passing value to api

Things i did :
I have one url api which will display some data in my table view.
2.And i fetch the particular value called "_id" from my url and i just pass that value to one custom class object.
Then i am passing that class object value to one of my another api calling url.In there i getting the value as nil.
Here is the code that ia m populating the data to my table view.And i am taking the particular value from api "_id" And i am storing that value to one custom class object :
// Showing favourites
func populateFavourites()
{
let token = NSUserDefaults.standardUserDefaults().valueForKey("access_token") as! String
let headers = ["x-access-token": token]
let request = NSMutableURLRequest(URL: NSURL(string: "some url")!)
request.HTTPMethod = "GET"
request.allHTTPHeaderFields = headers
let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if (error != nil)
{
print(error)
}
else
{
dispatch_async(dispatch_get_main_queue(),{
if let json = (try? NSJSONSerialization.JSONObjectWithData(data!, options: [])) as? Dictionary<String,AnyObject>
{
print("Success Got Json Data")
let success = json["success"] as? Int
if(success == 1)
{
let jsonData = json["data"] as! NSArray
if(jsonData.count>0)
{
for items in jsonData
{
let BObj = items as! NSDictionary
let BusinessDetails = BObj.valueForKey("business_id") as! Dictionary<String,AnyObject>
self.FavBusinessValues.append(Businessdata(json:BusinessDetails))
print(items)
let FavId = BObj.valueForKey("_id") as! String
self.FavDelete?.FavIds = FavId
print(FavId)
}
self.tableView.reloadData()
}
else
{
self.CreateNoDataLabel()
}
}
else
{
let message = json["message"] as? String
print("Fav Message ",message)
let alert = UIAlertController(title: "Error: ", message: message, preferredStyle: UIAlertControllerStyle.Alert)
// add an action (button)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
// show the alert
self.presentViewController(alert, animated: true, completion: nil)
}
}
})
}
})
dataTask.resume()
}
In my above code i am taking the particular value "_id" and i am passing to my custom objet class :
let FavId = BObj.valueForKey("_id") as! String
self.FavDelete?.FavIds = FavId
print(FavId)
This print(FavId) is printing the correct value.Here is my cutom class object that store that value of "_id" :
import Foundation
class Favdelete
{
var FavIds : String?
init(json:String)
{
self.FavIds = json as? String
}
}
Then i am passing the "_id" objet value FavIds to my one of my api calling.That code is under below :
func apidelete () {
let Favouriteid = FavDelete?.FavIds
print(Favouriteid)
let userToken = NSUserDefaults.standardUserDefaults().valueForKey("access_token") as! String;
print(userToken)
let headers = [
"x-access-token": userToken,
"cache-control": "no-cache",
"postman-token": "81fbd1fc-c333-3ceb-272a-a9ecffb2dad2",
"content-type": "application/x-www-form-urlencoded"
]
let FBI = "favourite_id="+Favouriteid!
let postData = NSMutableData(data: FBI.dataUsingEncoding(NSUTF8StringEncoding)!)
let request = NSMutableURLRequest(URL: NSURL(string: "some url")!,
cachePolicy: .UseProtocolCachePolicy,
timeoutInterval: 10.0)
let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? NSHTTPURLResponse
print(httpResponse)
dispatch_async(dispatch_get_main_queue(),{
if let json = (try? NSJSONSerialization.JSONObjectWithData(data!, options: [])) as? Dictionary<String,AnyObject>
{
print("Success Got Json Data");
let success = json["success"] as? Int;
var message:String = "";
if(success == 1)
{
message = json["message"] as! String;
}
else
{
message = json["message"] as! String;
}
print(message);
let alert = UIAlertController(title: "Message from server", message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
})
}
})
dataTask.resume()
}
When i try to print this :
let Favouriteid = FavDelete?.FavIds
print(Favouriteid)
I ma getting crash and it says nil printing in my console.
Can any one please help me out.!!
Updated :
Under my first function method.That is when i get the particular value and store that value to my custom object .I just print it.I am getting nil there too.
The line is :
let FavId = BObj.valueForKey("_id") as! String
self.FavDelete?.FavIds = FavId
print(self.FavDelete?.FavIds)
Its printing nil
Updated :
Although you have a custom class you need to initialize it to use its method. Without proper initialization you cannot set its properties.
So in the ViewController, declare the object for that class at the top.
var FavbDelete : Favdelete?
At this point, your FavDelete is nil.
Then you get the "_id" from populateFavourites() method. You may be calling it from viewDidLoad() method. There you get FabId as
let FavId = BObj.valueForKey("_id") as! String
But now you cannot directly store it into FabDelete Object. because it has not been initialized. Although Xcode doesn't throw any error when you do that, But the resulting value will be nil. So now you initialize that object using the initializer you built in custom class.
self.FavDelete = Favdelete(json: FavId)
Now the FavDelete object is initialized , and when you call apidelete() method after this step, you can access the value of id by using
let someVar = self.FavDelete?.FavIds
Since, all of this work is being done in background using NSURLSession,ensure that you are not calling apidelete before the FabDelete Object is initialized.

Resources