Labels in viewcontroller are populated very slow - ios

I'm struggling with extreme slow population of the labels in my viewcontroller.
The script sends a userid to a php script. To script retrieves data related to the userid in an array and sends it back as a json response to my app. Then the labels in my viewcontroller are populated with the corresponding data from the json array (dictionary).
the print statements in the "do{}" are executed immediately in the console, but the population of the labels in the viewcontroller takes ages. Putting self.lblStreet.text = self.loadedStreet outside do{}, but still in the task, it gives the same slow result.
class CharDetailViewController: UIViewController {
//Outlets
#IBOutlet weak var lblUID: UILabel!
#IBOutlet weak var lblStreet: UILabel!
//URL to our web service
let URL_GET_DETAILS = "http://somesite.com/script.php"
var passedUID: String!
var loadedStreet: String!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: URL_GET_DETAILS)
lblUID.text = passedUID
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "POST";
let sendUID = lblUID.text
let postParameters = "sendUID="+sendUID!;
request.httpBody = postParameters.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with:request as URLRequest){
data, response, error in
if error != nil{
print("error is \(error)")
return;
}
do{
let myJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = myJSON {
var uid : String!
uid = parseJSON["userid"] as! String?
self.loadedStreet = parseJSON["street"] as! String!
self.lblStreet.text = parseJSON["street"] as! String!
print(uid)
print(self.loadedStreet)
}
}
catch {
print(error)
}
}
task.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

Update your UI on the main thread. URLSession has you in a background thread. The appropriate code in Swift 3 is
DispatchQueue.main.async {
//your UI code
}
So your code should look more like this.
class CharDetailViewController: UIViewController {
//Outlets
#IBOutlet weak var lblUID: UILabel!
#IBOutlet weak var lblStreet: UILabel!
//URL to our web service
let URL_GET_DETAILS = "http://somesite.com/script.php"
var passedUID: String!
var loadedStreet: String!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: URL_GET_DETAILS)
lblUID.text = passedUID
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "POST";
let sendUID = lblUID.text
let postParameters = "sendUID="+sendUID!;
request.httpBody = postParameters.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with:request as URLRequest){
data, response, error in
if error != nil{
print("error is \(error)")
return;
}
do{
let myJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = myJSON {
var uid : String!
uid = parseJSON["userid"] as! String?
DispatchQueue.main.async {
//your UI code
self.loadedStreet = parseJSON["street"] as! String!
self.lblStreet.text = parseJSON["street"] as! String!
}
print(uid)
print(self.loadedStreet)
}
}catch {
print(error)
}
}
task.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

You shouldn't edit the UI anywhere other than the main thread. You can use this code to get the main thread in order to implement UI updates.
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI stuff here
});

Related

How to display YouTube search API response in UI text view?

I am creating a project where I have to display the YouTube search API response in a UI Text View when a button is clicked. Now I am getting the response in console.
1) How to display it as a JSON response.
2) How to display it in UI text view.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var resultTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func goButton(_ sender: Any) {
guard let url = URL(string: "https://www.youtube.com/watch?v=6Zf79Ns8_oY")else {
return
}
let session = URLSession.shared
let task = session.dataTask(with: url) {(data, response, error) in
if let response = response {
print(response)
}
if let jsondata = data {
print(jsondata)
}
}
task.resume()
}
}
I think you are looking for that:
Parse Response using JSONSerialization and set required value to textview text.
guard let url = URL(string: "https://www.youtube.com/watch?v=6Zf79Ns8_oY")else {
return
}
let session = URLSession.shared
let task = session.dataTask(with: url) {(data, response, error) in
guard let data = data, error == nil else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String:Any]
let text = json["KEY"] as? String
DispatchQueue.main.async{
self.YOURTEXTVIEW.text = text
}
} catch let error as NSError {
print(error)
}
}
task.resume()
maybe it is helpful.

Table only shows when interacting and not by default

I have a table in a view controller that is populated through a dictionary from which information is retrieved via a JSON request. In the viewDidLoad() function, I call the function that retrieves the data which is added to `IncompletedDeadlines dictionary:
override func viewDidLoad() {
super.viewDidLoad()
self.IncompleteDeadlines = [String:AnyObject]()
self.retrieveIncompletedDeadlines()
}
Everything works however the table only shows when interacted with. I thought maybe the best way to show the table the moment the view appears is by adding a tableView.reload to viewDidAppear as so:
override func viewDidAppear(_ animated: Bool) {
self.tableView.reloadData()
}
But this doesn't fix it. I have attached pictures for clarity of the situation. Picture one shows the view the moment the view appears. Picture 2 only happens once the table is interacted with i.e. swiped. So my question is how can I get the table to show immediately? I understand there can be a delay because of the load, but I shouldn't have to interact with it for it to show:
When the view is interacted with i.e. swiped:
The retrieveIncompletedDeadlines() function is as so:
func retrieveIncompletedDeadlines(){
let myUrl = NSURL(string: "https://www.example.com/scripts/retrieveIncompleteDeadlines.php");
let request = NSMutableURLRequest(url:myUrl! as URL)
let user_id = UserDetails[0]
request.httpMethod = "POST";
let postString = "user_id=\(user_id)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(String(describing: error))")
return
}
var err: NSError?
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
let checker:String = parseJSON["status"] as! String;
if(checker == "Success"){
let resultValue = parseJSON["deadlines"] as! [String:AnyObject]
self.IncompleteDeadlines = resultValue
}
self.tableView.reloadData()
}
} catch let error as NSError {
err = error
print(err!);
}
}
task.resume();
self.tableView.reloadData()
}
JSON will be parsed on the background thread but any update to the UI must be done on the main thread hence you have to do it inside DispatchQueue.main.async {} This article explains well what is the problem.
Furthermore I would write a completions handler which returns the data once the operation has finished. This is another interesting article about.
Completion handlers are super convenient when your app is doing something that might take a little while, like making an API call, and you need to do something when that task is done, like updating the UI to show the data.
var incompleteDeadlines = [String:AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
//please note your original function has changed
self.retrieveIncompletedDeadlines { (result, success) in
if success {
// once all the data has been parsed you assigned the result to self.incompleteDeadlines
self.incompleteDeadlines = result
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
func retrieveIncompletedDeadlines(_ completion:#escaping ([String:AnyObject] , _ success: Bool)-> Void){
let myUrl = NSURL(string: "https://www.example.com/scripts/retrieveIncompleteDeadlines.php");
let request = NSMutableURLRequest(url:myUrl! as URL)
let user_id = UserDetails[0]
request.httpMethod = "POST";
let postString = "user_id=\(user_id)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(String(describing: error))")
return
}
var err: NSError?
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
let checker:String = parseJSON["status"] as! String;
var resultValue = [String:AnyObject]()
if(checker == "Success"){
resultValue = parseJSON["deadlines"] as! [String:AnyObject]
}
completion(resultValue, true)
}
} catch let error as NSError {
err = error
print(err!);
}
}
task.resume();
}
}

Why do I need to use dispatch_async I think I am on the same thread

I have a login page in IOS using swift. When I click the SignIn button I start the login process that calls a function with delegate call backs on completion. This works fine, and my activity indicator starts spinning. The issue is, that when I get my delegate callbacks and I call the stopanimating() on the indicator it does not stop until about 6 seconds later. If I add the dispatch_async, it will stop animating right away. This makes me believe that I am not running on the same thread, but I do not see where this is happening.
EDIT: It looks like when I am calling in my Rest Function session.dataTaskWithRequest() that the block of code that is returned is not on the main thread, so when I call the delegates and return to the main code, it is not on the main thread? Is this correct? I can't find documentation that would explain that.
class Login_ViewController: UIViewController, RestServiceDelegate {
#IBOutlet weak var txtEmail: UITextField!
#IBOutlet weak var txtPassword: UITextField!
#IBOutlet weak var signInActivity: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func signInClick(sender: UIButton) {
signInActivity.startAnimating()
var restReq = RestRequest()
restReq.delegate = self
var restParams = ["userEmail":"\(txtEmail.text)", "password":"\(txtPassword.text)"] as Dictionary<String, String>
restReq.callRestAction(restParams, restAction: "AuthenticateUser")
}
func stopActivity()
{
println("Stopping the Activity Indiciator")
// WHY DOES THIS LINE TAKE FOREVER TO STOP ANIMATING?
self.signInActivity.stopAnimating()
// IF I UNCOMMENT THIS NEXT LINE IT WILL STOP THE ANIMATION!
//dispatch_async(dispatch_get_main_queue() , {self.signInActivity.stopAnimating()})
}
// MARK: - RestServiceDelegate
func restServiceEndedWithError(message : String, calledRestAction : String) {
stopActivity()
println("Error with Rest Request: \(message) for Action: \(calledRestAction)")
}
func restServiceResponse(jsonResponse : NSDictionary, calledRestAction : String) {
println("Response from Rest Request \(calledRestAction) with data \(jsonResponse)")
if let userAuth : AnyObject = jsonResponse["AuthenticateUserResult"] {
if userAuth as! Bool == true
{
println("User Authenticated: \(userAuth).")
}
else
{
println("Not Authenticated")
}
} else {
println("Not Authenticated")
}
stopActivity()
}
}
Here is the code when calling the Rest Action:
protocol RestServiceDelegate
{
func restServiceResponse(jsonResponse : NSDictionary , calledRestAction : String)
func restServiceEndedWithError(message : String, calledRestAction : String)
}
class RestRequest
{
let appToken : String = "blah blah blah"
let restURL : String = "http://blah blah blah"
var delegate : RestServiceDelegate?
func callRestAction(params : Dictionary<String,String>, restAction : String )
{
var url : String = restURL + restAction
println("CALLING REST URL: \(url)")
var request : NSMutableURLRequest = NSMutableURLRequest()
request.URL = NSURL(string: url)
request.HTTPMethod = "POST"
var session = NSURLSession.sharedSession()
var newParams = params
newParams.updateValue(appToken, forKey: "appToken")
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(newParams, options: nil, error: &err)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println("Response: \(response)")
var strData : String = (NSString(data: data, encoding: NSUTF8StringEncoding) as? String)!
println("Returned Result (BODY): \(strData)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &err) as? NSDictionary
if(err != nil)
{
self.delegate!.restServiceEndedWithError(err!.localizedDescription, calledRestAction: restAction)
}
else
{
self.delegate!.restServiceResponse(json!, calledRestAction: restAction)
}
})
task.resume()
}
}

Can't use variable outside of scope method in Swift (dataTaskWithRequest)

#IBOutlet var nameLabel : UILabel!
var finalString: String = "test"
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.
}
#IBAction func helloWorldAction(nameTextField: UITextField) {
//fetch data from server
let request = NSMutableURLRequest(URL: NSURL(string: "http://192.168.1.11")!)
request.HTTPMethod = "POST"
let postString = "user=test&pass=test3"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
//error handeling
if error != nil {
println("error=\(error)")
return
}
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
self.finalString = String(responseString!)
println("\(self.finalString)");
}
task.resume()
//print finalString
println("finalString = \(finalString)")
}
}
I am trying to do two things, and I will tell you what isn't working with both.
First, not seen in this code, I was trying to assign a UILabel.text a value, that didn't work at all. I couldn't do it within the function and neither could I do it outside. This brings me to problem number two. When finalString is printed inside the function it outputs the proper value.
However, when its printed outside the function it prints the value it was first assigned. Please tell me how to assign the UILabel.text a value properly and how to use the output outside of the scope of the questions.
Thanks in advance.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var nameLabel: UITextField!
// you have to add a completion block to your asyncronous request
func fireRequest(link:String,completion: ((data: NSData?) -> Void)) {
if let requestUrl = NSURL(string: link){
let request = NSMutableURLRequest(URL: requestUrl)
request.HTTPMethod = "POST"
let postString = "user=test&pass=test3"
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()
}
}
override func viewDidLoad() {
super.viewDidLoad()
println("Fired request" + NSDate().description )
fireRequest("http://192.168.1.11") { data in
dispatch_async(dispatch_get_main_queue()) {
println("Finished request")
if let data = data { // unwrap your data (!= nil)
let myResponseStr = NSString(data: data, encoding: NSUTF8StringEncoding) as String
self.nameLabel.text = myResponseStr
println("response:"+myResponseStr)
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

Connect to an online database (mySQL) from Swift, using PHP. (XCODE)

I want to connect my xcode app to online database and get the data from it and display in my app + write the data into online database using my app. I've already done with app but now it gives me an error.
ERROR :
I have my online database in my web page and i have uploaded two php files into the file manager in my web. One php file retrieving all the data in my database and encoding them to json. And second php file doing the query to write data into my online database from my app.
As in above pic im getting json output successfully but when i try to get the data into an array in xcode it gives me that error.
This is my code
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableview: UITableView!
#IBOutlet var inputFriendName: UITextField!
#IBOutlet var inputFriendInfo: UITextField!
var data: NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
data = dataOfJson("http://bishanonline.com/extra/serviceselect.php")
println(data)
}
#IBAction func reload() {
data = dataOfJson("http://bishanonline.com/extra/serviceselect.php")
self.tableview.reloadData()
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
self.view.endEditing(true)
}
func dataOfJson(url: String) -> NSArray {
var data = NSData(contentsOfURL: NSURL(string: url)!)
return (NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as NSArray)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: additionInfoCell = self.tableview.dequeueReusableCellWithIdentifier("customCell") as additionInfoCell
var maindata = (data[indexPath.row] as NSDictionary)
cell.friendName!.text = maindata["Name"] as? String
cell.friendInfo!.text = maindata["Additional Info"] as? String
return cell
}
#IBAction func uploadToDatabase() {
var url: NSString = "http://bishanonline.com/extra/servicequery.php?x=\(inputFriendName.text)&y=\(inputFriendInfo.text)"
url = url.stringByReplacingOccurrencesOfString(" ", withString: "%20")
url = url.stringByReplacingOccurrencesOfString("/n", withString: "%0A")
var data = NSData(contentsOfURL: NSURL(string: url)!)
var result = NSString(data: data!, encoding: NSUTF8StringEncoding)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Issue is in this code lines
func dataOfJson(url: String) -> NSArray {
var data = NSData(contentsOfURL: NSURL(string: url)!)
return (NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as NSArray)
}
Please help me to get json data into array. Appreciate any help.
Finally problem resolved.First i am going to elaborate the exact problem then the solution will be posted.
The code you were doing was totally fine but the real problem was your backend
For serviceselect.php
The code you have done for fetching records is
func dataOfJson(url: String) -> NSArray
{
var data = NSData(contentsOfURL: NSURL(string: url)!)
return (NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as NSArray)
}
This above method is returing NSArray but the data you are getting from the server is kinda messed up because along with JSON data some garbage data is included as well.Check out the below image
So when try to generate JSON data from above string we are getting crashes and errors.
May be due to free hosting service we are getting this message (Not sure)
Solution
func getallrecords(){
let url = NSURL(string: "http://bishanonline.com/extra/serviceselect.php")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
var d = NSString(data: data, encoding: NSUTF8StringEncoding)
var arr = d!.componentsSeparatedByString("<") // spliting the incoming string from "<" operator because before that operator is our required data and storing in array
var dataweneed:NSString = arr[0] as NSString // arr[0] is the data before "<" operator and arr[1] is actually no use for us
if let data = NSJSONSerialization.JSONObjectWithData(dataweneed.dataUsingEncoding(NSUTF8StringEncoding)!, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSArray
// JSONObjectWithData always have first argument of NSData but our dataweneed is actually NSString so we are actually converting NSString to NSData
{
for dd in data{
var name : String = dd["Name"]! as String
var info : String = dd["Additional Info"]! as String
println("Name is : \(name)") // MainDeveloper for 0 and BestBuddy for 1 index
println("Info is : \(info)") // Bishan for 0 and AkilaPrabath for 1 index
}
}
}
task.resume()
}
Final output
For servicequery.php
func addrecord(x:String,y:String){
let request = NSMutableURLRequest(URL: NSURL(string: "http://bishanonline.com/extra/servicequery.php")!)
var postString : String = "x="+x+"&y="+y
request.HTTPMethod = "GET"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
if jsonResult as String == "Successfully added "
{
// Show an alert to notify user
}
}
task.resume()
}
Also remove "echo $query;" on line 30 of servicequery.php
Try this code to parse JSON from server
//created NSURL
let requestURL = NSURL(string: URL_GET_TEAMS)
//creating NSMutableURLRequest
let request = NSMutableURLRequest(URL: requestURL!)
//setting the method to post
request.HTTPMethod = "GET"
//creating a task to send the post request
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
//exiting if there is some error
if error != nil{
print("error is \(error)")
return;
}
//parsing the response
do {
//converting resonse to NSArray
let myJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSArray
//looping through all the json objects in the array teams
for i in 0 ..< myJSON.count{
myJSON[i]["object key here"]
}
} catch {
print(error)
}
}
//executing the task
task.resume()
Source: json parsing in ios swift

Resources