swift what is the timing to reload data after NSURLSession POST request - ios

I am trying to show some data in tableView after getting JSON. However, it is failed by using tableView.reloadData() . I menu delay.
My situation is that the tableView will reload data after 14-20 seconds of getting JSON, but it will reload data immediately when user active the table.
Swift:
func getJson(word: String){
let url: NSURL = NSURL(string: "MyPHP.php")!
let session = NSURLSession.sharedSession()
let request:NSMutableURLRequest = NSMutableURLRequest(URL:url)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let bodydata = "label=\(word)"
request.HTTPBody = bodydata.dataUsingEncoding(NSUTF8StringEncoding)
if bodydata != " " {
let task = session.dataTaskWithRequest(request) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if(statusCode == 200) {
self.libraryArray.removeAll()
do{
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
print(json)
if let book = json["Book"] as? [[String: AnyObject]]{
for books in book {
if let BookName = book["Name"] as? [String: AnyObject] {
let name = BookName["name"] as? String
let author = BookName["author"] as? String
let bookDict = [
"name": name,
"id": id,
"author": author]
self.libraryArray.append(bookDict)
self.tableView.reloadData() // has 14- 20 seconds delay, but the JSON has already received
}
}
}
}catch {
print("error:\(error)")
}
}
}
task.resume()
// self.tableView.reloadData() if i try to reload here, it will not be executed.
}
}
#IBAction func Next(sender: AnyObject) {
self.getJson("someString")
//self.tableView.reloadData() if i try to reload here, it will be fail.
}
what is the problem of the delay? How to solve it?
What is the right timing to reload tableView?

Replace your self.tableView.reloadData() with this
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})

You are asking the table to reload while you didn't finish parsing the json. you do that after every Book parsing. you should do it after you finish parsing. the only explanation of the delay is that you have a huge data that is taking all that time for parsing!! Is that possible?
func getJson(word: String){
let url: NSURL = NSURL(string: "MyPHP.php")!
let session = NSURLSession.sharedSession()
let request:NSMutableURLRequest = NSMutableURLRequest(URL:url)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let bodydata = "label=\(word)"
request.HTTPBody = bodydata.dataUsingEncoding(NSUTF8StringEncoding)
if bodydata != " " {
let task = session.dataTaskWithRequest(request) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if(statusCode == 200) {
self.libraryArray.removeAll()
do{
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
print(json)
if let book = json["Book"] as? [[String: AnyObject]]{
for books in book {
if let BookName = book["Name"] as? [String: AnyObject] {
let name = BookName["name"] as? String
let author = BookName["author"] as? String
let bookDict = [
"name": name,
"id": id,
"author": author]
self.libraryArray.append(bookDict)
self.tableView.reloadData() // has 14- 20 seconds delay, but the JSON has already received
}
}
}
}catch {
print("error:\(error)")
}
self.tableView.reloadData() // <<----- HERE
}
}
task.resume()
// self.tableView.reloadData() if i try to reload here, it will not be executed.
}
}
#IBAction func Next(sender: AnyObject) {
self.getJson("someString")
//self.tableView.reloadData() if i try to reload here, it will be fail.
}

Related

Using POST request in HTTP method holds the UI?

When I tap on button it will perform a service request operation.Based on the result it will redirect to next view controller.
After loading Next view controller holds or block the UI. How to solve this issue ? I am using RestAPI and GCD first time in swift, so don't know how to solve this.....
This is login button
#IBAction func btnLogin(_ sender: Any)
{
self.api()
}
This is the function what we call.
func api()
{
let myURL = URL(string: "http://www.digi.com/laravel_api_demo/api/demoapipost")
let request = NSMutableURLRequest(url: myURL!)
request.httpMethod = "POST"
let strEmail = tfLgnID.text
let strPwd = tfPwd.text
let postString = ["username":strEmail, "password":strPwd]
//let postString = ["username":"ayush", "password":"abc"]
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
//create the session object
//let session = URLSession.shared
do {
request.httpBody = try JSONSerialization.data(withJSONObject: postString, options: .prettyPrinted) // pass dictionary to nsdata object and set it as request body
print("Successfully passed data to server")
} catch let error {
print(error.localizedDescription)
}
let postTask = URLSession.shared.dataTask(with: request as URLRequest) { (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("POST Method :\(json)")
let dict = json as? [String: Any]
let num = dict!["status"]
print("Status : \(num)")
print("Dict : \(dict)")
print("username : \(dict!["username"])")
print("password : \(dict!["password"])")
if dict!["status"] as! Int == 1
{
print("Successfully Logged In")
DispatchQueue.main.async {
let visitorVC = self.storyboard?.instantiateViewController(withIdentifier: "VisitorVC") as! VisitorVC
self.present(visitorVC, animated: true, completion: nil)
}
print("OK")
}
else
{
print("Not OK")
}
// handle json...
}
} catch let error {
print(error.localizedDescription)
}
}
postTask.resume()
}
Try this method
func api() {
var request = URLRequest(url: URL(string: "")!) // put your url
request.httpMethod = "POST"
let strEmail = tfLgnID.text
let strPwd = tfPwd.text
let postString:String = "user_id=\(strEmail)&user_id=\(strPwd)"
request.httpBody = postString.data(using: .utf8)
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
}
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary {
print(jsonResult)
let status = jsonResult["status"]! as! NSString
print("status\(status)")
DispatchQueue.main.async(execute: {
// your error Alert
})
}
else {
DispatchQueue.main.async(execute: {
let visitorVC = self.storyboard?.instantiateViewController(withIdentifier: "VisitorVC") as! VisitorVC
self.present(visitorVC, animated: true, completion: nil)
})
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
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))")
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(String(describing: responseString))")
}
task.resume()
}

How to parse a api for swift 3?

Have been researching on the parsing for quite a bit. With plethora of information avilable for JSON nothing seems to explain how to do in a sensible way to extract information with swift 3.
This is what got so far
func getBookDetails() {
let scriptUrl = "https://www.googleapis.com/books/v1/volumes?q=isbn:9781451648546" .
let myurl = URL(string:scriptUrl)
let request = NSMutableURLRequest(url: myurl!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: myurl! ) { (data, response, error) in
if error != nil{
print("THIS ERROR",error!)
return
} else{
if let mydata = data{
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let dictonary = myJson["items"] as AnyObject? {
print("the DICTONARY",dictonary) // ----> OUTPUT
if let dictonaryAA = dictonary["accessInfo"] as AnyObject? {
print("the accessInfo",dictonaryAA)
}
}
} catch{
print("this is the in CATCH")
}
} //data
}
}
task.resume()
}
}
OUTPUT :
the DICTONARY (
{
accessInfo = {
accessViewStatus = SAMPLE;
country = US;
=============
RELEVANT DATA as in https://www.googleapis.com/books/v1/volumes?
q=isbn:9781451648546"
==========================
title = "Steve Jobs";
};
}
)
Just need to parse through the json data to get the name, author and title of the book with reference to isbn.
Know there should be a better way to do things that is easily understandable to someone new into the language
You can parse the api in two ways
Using URLSession:
let rawDataStr: NSString = "data={\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"
self.parsePostAPIWithParam(apiName: "get_posts", paramStr: rawDataStr){ ResDictionary in
// let statusVal = ResDictionary["status"] as? String
self.postsDict = (ResDictionary["posts"] as! NSArray!) as! [Any]
print("\n posts count:",self.postsDict.count)
}
func parsePostAPIWithParam(apiName:NSString, paramStr:NSString,callback: #escaping ((NSDictionary) -> ())) {
var convertedJsonDictResponse:NSDictionary!
let dataStr: NSString = paramStr
let postData = NSMutableData(data: dataStr.data(using: String.Encoding.utf8.rawValue)!)
let request = NSMutableURLRequest(url: NSURL(string: "http://13.12..205.248/get_posts/")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = nil
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse as Any)
do{
if let convertedJsonIntoDict = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
convertedJsonDictResponse = convertedJsonIntoDict.object(forKey: apiName) as? NSDictionary
// callback for response
callback(convertedJsonDictResponse)
}
} catch let error as NSError {
print(error)
}
}
Using Alamofire
func AlamofirePOSTRequest() {
let urlString = "http://13.12..205.../get_posts/"
let para = ["data": "{\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"]
Alamofire.request(urlString, method: .post, parameters: para , headers: nil).responseJSON {
response in
switch response.result {
case .success:
print("response: ",response)
let swiftyJsonVar = JSON(response.result.value!)
if let resData = swiftyJsonVar["posts"].arrayObject {
self.postsDict = resData as! [[String:AnyObject]]
}
print("\n \n alomafire swiftyJsonVar: ",swiftyJsonVar)
break
case .failure(let error):
print(error)
}
}
}
})
dataTask.resume()
}
First of all, all JSON types are value types in Swift 3 so the most unspecified type is Any, not AnyObject.
Second of all, there are only two collection types in the JSON type set, dictionary ([String:Any]) and array ([Any], but in most cases [[String:Any]]). It's never just Any nor AnyObject.
Third of all, the given JSON does not contain a key name.
For convenience let's use a type alias for a JSON dictionary:
typealias JSONDictionary = [String:Any]
The root object is a dictionary, in the dictionary there is an array of dictionaries for key items. And pass no options, .mutableContainers is nonsense in Swift.
guard let myJson = try JSONSerialization.jsonObject(with: mydata) as? JSONDictionary,
let items = myJson["items"] as? [JSONDictionary] else { return }
Iterate through the array and extract the values for title and authors which is an array by the way. Both values are in another dictionary for key volumeInfo.
for item in items {
if let volumeInfo = item["volumeInfo"] as? JSONDictionary {
let title = volumeInfo["title"] as? String
let authors = volumeInfo["authors"] as? [String]
print(title ?? "no title", authors ?? "no authors")
The ISBN information is in an array for key industryIdentifiers
if let industryIdentifiers = volumeInfo["industryIdentifiers"] as? [JSONDictionary] {
for identifier in industryIdentifiers {
let type = identifier["type"] as! String
let isbn = identifier["identifier"] as! String
print(type, isbn)
}
}
}
}
You are doing wrong in this line
if let dictonaryAA = dictonary["accessInfo"] as AnyObject?
because dictonary here is an array not dictionary. It is array of dictionaries. So as to get first object from that array first use dictonary[0], then use accessInfo key from this.
I am attaching the code for your do block
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let array = myJson["items"] as AnyObject? {
print("the array",array) // ----> OUTPUT
let dict = array.object(at: 0) as AnyObject//Master Json
let accessInf = dict.object(forKey: "accessInfo") //Your access info json
print("the accessInfo",accessInf)
}
}
Hope this helps you.

Swift - View loads before http request is finished in viewDidLoad()

I am trying to load a values from a database and put them into a UITableView in the viewDidLoad function in one of my Swift files. When debugging, at the time of the view rendering, the list of values is empty, but after the view loads, the list gets populated by the view loads. I don't have much experience with threads in Swift, so I am not exactly sure why this is happening, any ideas? I have tried to run DispatchQueue.main.async, but that did not work My code is below:
override func viewDidLoad() {
super.viewDidLoad()
// Load any saved meals, otherwise load sample data.
loadDbMeals()
}
private func loadDbMeals() {
var dbMeals = [Meal]()
let requestURL = NSURL(string: self.URL_GET)
let request = NSMutableURLRequest(url: requestURL! as URL)
request.httpMethod = "GET"
//creating a task to send the post request
let task = URLSession.shared.dataTask(with: request as URLRequest){
data, response, error in
if error != nil{
print("error is \(String(describing: error))")
return;
}
//parsing the response
do {
//converting response to NSDictionary
let myJSON = try JSONSerialization.jsonObject(with: data!, options: [.mutableContainers]) as? NSDictionary
//parsing the json
if let parseJSON = myJSON {
if let nestedDictionary = parseJSON["message"] as? NSArray {
for meal in nestedDictionary {
if let nestedMeal = meal as? NSDictionary {
let mealName = nestedMeal["name"]
let rating = nestedMeal["rating"]
dbMeals.append(Meal(name: mealName as! String, photo: UIImage(named: "defaultPhoto"), rating: rating as! Int, ingredientList: [])!)
}
}
}
}
} catch {
print(error)
}
}
meals += dbMeals
//executing the task
task.resume()
}
So, the current order of breakpoints, is the call to loadDbMeals() in the viewDidLoad() function, then it tries to add the dbMeals variables to the global meals variable, and then the http request gets executed, after the empty list has already been added. I appreciate any help!
Reload your table after loading data
if let parseJSON = myJSON {
if let nestedDictionary = parseJSON["message"] as? NSArray {
for meal in nestedDictionary {
if let nestedMeal = meal as? NSDictionary {
let mealName = nestedMeal["name"]
let rating = nestedMeal["rating"]
dbMeals.append(Meal(name: mealName as! String, photo: UIImage(named: "defaultPhoto"), rating: rating as! Int, ingredientList: [])!)
}
}
DispatchQueue.main.async{
self.tableView.reloadData()
}
}
}
the request happens asynchronously. so the view is loaded while the request may still be in progress.
move the meals += dbMeals line into the request's completion handler (after the for loop), add a self. to the meals var since you are referencing it from within a closure and reload the tableview from the main thread afterwards:
DispatchQueue.main.async{
self.tableView.reloadData()
}
Because dataTask is not a synchronised call, we need to use lock to wait until all fetch is finished.
Code will look something like this:
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global().async {
let lock = DispatchSemaphore(value: 0)
// Load any saved meals, otherwise load sample data.
self.loadDbMeals(completion: {
lock.signal()
})
lock.wait()
// finished fetching data
}
}
private func loadDbMeals(completion: (() -> Void)?) {
var dbMeals = [Meal]()
let requestURL = NSURL(string: self.URL_GET)
let request = NSMutableURLRequest(url: requestURL! as URL)
request.httpMethod = "GET"
//creating a task to send the post request
let task = URLSession.shared.dataTask(with: request as URLRequest){
data, response, error in
if error != nil{
print("error is \(String(describing: error))")
return;
}
//parsing the response
do {
//converting response to NSDictionary
let myJSON = try JSONSerialization.jsonObject(with: data!, options: [.mutableContainers]) as? NSDictionary
//parsing the json
if let parseJSON = myJSON {
if let nestedDictionary = parseJSON["message"] as? NSArray {
for meal in nestedDictionary {
if let nestedMeal = meal as? NSDictionary {
let mealName = nestedMeal["name"]
let rating = nestedMeal["rating"]
dbMeals.append(Meal(name: mealName as! String, photo: UIImage(named: "defaultPhoto"), rating: rating as! Int, ingredientList: [])!)
}
}
}
}
} catch {
print(error)
}
// call completion
completion()
}
meals += dbMeals
//executing the task
task.resume()
}
So execute loadDbMeals with completion block which will be called when fetching is finished and lock will wait until completion block is called.

HTTP request in swift 3 Xcode 8.3

I am getting stuck with HTTP request.it did not show any error.compiler reads the first two lines and skip the code to "task.resume()".i am fetching data with same code on other view controller but it creats problem here
func getCustomers()
{
let url = NSURL(string: "myURL.com")
let task = URLSession.shared.dataTask(with: url! as URL) {
(data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
print("error: \(String(describing: error))")
return
}
do
{
self.getcustomersArray = [GetCustomers]()
//JSON Parsing
if let data = data,
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
{
let results = json["Result"] as? [[String : Any]]
let getCustomersObject:GetCustomers = GetCustomers()
for result in results!
{
getCustomersObject.ActivityPrefix = (result["ActivityPrefix"] as? String)!
getCustomersObject.CustomerID = (result["CustomerID"] as? String)!
getCustomersObject.CustomerName = (result["CustomerName"] as? String)!
getCustomersObject.TFMCustomerID = (result["TFMCustomerID"] as? String)!
getCustomersObject.ShortName = (result["ShortName"] as? String)!
getCustomersObject.UserRights = (result["UserRights"] as? Int)!
self.totalCustomers += self.totalCustomers
}
self.customerName = getCustomersObject.CustomerName
}
}//end Do
catch
{
}
}
task.resume()
}
Using Block GET/POST/PUT/DELETE:
let request = NSMutableURLRequest(url: URL(string: "Your API URL here" ,param: param))!,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval:"Your request timeout time in Seconds")
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers as? [String : String]
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest) {data,response,error in
let httpResponse = response as? HTTPURLResponse
if (error != nil) {
print(error)
} else {
print(httpResponse)
}
DispatchQueue.main.async {
//Update your UI here
}
}
dataTask.resume()
I think you dont mention line
request.httpMethod = "GET"

NSURL Request is not working

I have a request, after it I need a full Dictionary. But when I checked my request with breakpoints, I saw that it jump over my request... What is wrong in it?
func downloadPackages() {
let url = "\(URL_BASE)package/list?id=666"
let dataURL = NSURL(string: url)
let dataRequest = NSURLRequest(URL: dataURL!)
let dataSession = NSURLSession.sharedSession()
print("QQ")
let dataTask = dataSession.dataTaskWithRequest(dataRequest) { (data, response, error) -> Void in
print("QQ2")
if error != nil {
print(error.debugDescription)
} else {
do {
let packageDict = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as? Dictionary<String, AnyObject>
if let results = packageDict!["data"] as? [Dictionary<String, AnyObject>] {
for obj in results {
let package = Package(packageDict: obj)
print(self.monsters.count)
self.monsters.append(package)
}
}
//Main UI thread
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
catch {
}
}
}
dataTask.resume()
}
It doesn't print ("QQ2"), but it prints ("QQ").
I think that you didn't add HTTP Request methods and values. So try like this :-
func downloadPackages() {
let url = "\(URL_BASE)package/list?id=666"
let dataURL = NSURL(string: url)
let dataRequest: NSMutableURLRequest = NSMutableURLRequest(URL: dataURL!)
dataRequest.HTTPMethod = "POST"
dataRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
dataRequest.setValue("*/*", forHTTPHeaderField: "Accept")
let dataSession = NSURLSession.sharedSession()
print("QQ")
let dataTask = dataSession.dataTaskWithRequest(dataRequest) { (data, response, error) -> Void in
print("QQ2")
if error != nil {
print(error.debugDescription)
} else {
do {
let packageDict = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as? Dictionary<String, AnyObject>
if let results = packageDict!["data"] as? [Dictionary<String, AnyObject>] {
for obj in results {
let package = Package(packageDict: obj)
print(self.monsters.count)
self.monsters.append(package)
}
}
//Main UI thread
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
catch {
}
}
}
dataTask.resume()
}
i think you made a syntax mistake:-
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
and yes wait for a sec for uitableview to be populated.
Following would be better way:-
dispatch_async(dispatch_get_main_queue(), {
for obj in results {
let package = Package(packageDict: obj)
print(self.monsters.count)
self.monsters.append(package)
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
})
rest seems to be fine.

Resources