0

I am having difficulties storing the results retrieved from a JSON source data. I have confirmed the ability to print the data retrieved but it was not able to store into my local array.

My end objective is to actually print in a UITableView the results.

Below is the code for my relevant table view controller :

import UIKit

class CommunityActivityTableViewController: UITableViewController {
    var displayNameArr = [String]()
    var postDateArr = [String]()
    var postDetailArr = [String]()
    var testArr = ["teaad"]

    override func viewDidLoad() {
        super.viewDidLoad()
        parseJson()
        print(self.displayNameArr.count) //returns 0
        print(self.postDateArr.count) //returns 0
        print(self.postDetailArr.count) //returns 0
        print(self.testArr.count)
        print("end")

    }
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections

        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        // #warning Incomplete implementation, return the number of rows

        return self.displayNameArr.count
    }



    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        print("3")
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell_activity", forIndexPath: indexPath)



        print("hi")
        cell.textLabel?.text = "hi"
        cell.detailTextLabel?.text = "test"

        return cell
    }
    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }
    override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }
    func makeAttributedString(title title: String, subtitle: String) -> NSAttributedString {
        let titleAttributes = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline), NSForegroundColorAttributeName: UIColor.purpleColor()]
        let subtitleAttributes = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)]

        let titleString = NSMutableAttributedString(string: "\(title)\n", attributes: titleAttributes)
        let subtitleString = NSAttributedString(string: subtitle, attributes: subtitleAttributes)

        titleString.appendAttributedString(subtitleString)

        return titleString
    }
    func parseJson(){
        //MARK: JSON parsing
        let requestURL: NSURL = NSURL(string: "<sanitised>")!
        let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
        let session = NSURLSession.sharedSession()
        let task = session.dataTaskWithRequest(urlRequest) {
            (data, response, error) -> Void in

            let httpResponse = response as! NSHTTPURLResponse
            let statusCode = httpResponse.statusCode

            if (statusCode == 200) {
                print("Everyone is fine, file downloaded successfully.")

                do{

                    let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)

                    if let results = json["result"] as? [[String: AnyObject]] {

                        for result in results {

                            if let lastname = result["last_name"] as? String {

                                if let postdate = result["timestamp"] as? String {
                                    if let firstname = result["first_name"] as? String {
                                        if let postdetails = result["post_details"] as? String {

                                            let displayname = firstname + " " + lastname
                                            //print(displayname)
                                            self.displayNameArr.append(displayname)
                                            self.postDateArr.append(postdate)
                                            self.postDetailArr.append(postdetails)
                                            self.testArr.append("haha")


                                        }
                                    }

                                }

                            }
                        }

                    }

                }catch {
                    print("Error with Json: \(error)")
                }

            }
        }

        task.resume()}


}

As per the code above the print results of displaynamearr.count and postDateArr.count and postDetailArr.count returned 0 when it should have returned more than 0 as a result of parseJson() method.

I have printed the display name, postgame and post details variables and they all contain data within so the problem does not lie with the extraction of data but the appending of data into the array.

Appreciate any help provided thanks ! Developed on Xcode 7 and Swift 2.2 Sanitised my JSON source due to sensitive nature of information (i have verified the retrieval of information is OK)

3
  • Your parseJSON method executes asynchronously. The NSURLSessionTask completion block executes only after the web request completes, which will happen after the method call has finished. When you print the arrays in viewDidLoad this has not occurred, so they are empty Commented Jun 19, 2016 at 9:02
  • Try calling self.reloadData after self.testArr.append("haha") Commented Jun 19, 2016 at 9:03
  • @RichTolley it didn't work and error returned "value of type controller has no member reloadData" Commented Jun 19, 2016 at 14:16

2 Answers 2

1

dataTaskWithRequest() is an asynchronous data loading. It loads on the background thread ensuring your UI won't freeze up. So your array will be empty when you this will be getting executed and hence your error. You need to a completion handler like so:

   func parseJson(completion: (isDone: Bool) -> ()){

///code

for result in results {

                            if let lastname = result["last_name"] as? String {

                                if let postdate = result["timestamp"] as? String {
                                    if let firstname = result["first_name"] as? String {
                                        if let postdetails = result["post_details"] as? String {

                                            let displayname = firstname + " " + lastname
                                            //print(displayname)
                                            self.displayNameArr.append(displayname)
                                            self.postDateArr.append(postdate)
                                            self.postDetailArr.append(postdetails)
  self.testArr.append("haha")


       }
completion(isDone: True)

   }



}

Now in viewDidLoad:

 override func viewDidLoad() {
            super.viewDidLoad()
            parseJson(){ success in
            if success{
            print(self.displayNameArr.count) //returns a value
            print(self.postDateArr.count) //returns a value
            print(self.postDetailArr.count) //returns a value
            print(self.testArr.count) //This wont because I havent added it in the completion handler
            print("end")
       self.tableView.reloadData()
   }
   }
    }
Sign up to request clarification or add additional context in comments.

6 Comments

this code does not work. returned error on func parseJson indicating unknown variable json: [JSON]?
this part "completion:(error: nil, results: json, postDate: postdate, postDetail: postDetails, displayName: displayname) " is not working as well... seems to be a syntax error
a new error returned for "completion(true)". the error is "missing argument label 'isDone:' in call
Edited. You need to do `completion(isDone: True). Try?
yes its working but my function "override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {" the displaynhamearr still returns as 0 how do i rectify that ? I just need to display the values of displayNameArr, postNameArr and postDetailsArr in the table view which is not happening as the return count is 0 :(
|
0

All of your UI updates run on the main thread. If you do something like

let task = session.dataTaskWithRequest(urlRequest) {
            (data, response, error) -> Void in
   // ...
}.resume()

you start a task asynchronously on another thread (not the main thread). Your iPhone is doing a network request and this takes some time. So I guess when your cellForRowAtIndexPath delegate method is called you haven't received any data yet. This is the reason you don't see anything.

The easiest solution to this would be to reload the table view once you have received the data. When you're done with all the parsing in your parseJson method (outside of all the loops) simply run:

dispatch_async(dispatch_get_main_queue()) {
   self.tableView.reloadData()  
}

This forces your table view to update. Remember that you have to run code that updates the UI on the main thread. This is what dispatch_async(dispatch_get_main_queue()) {} does.

EDIT: The answer above was to illustrate the problem to you. The more elegant solution would be to use a completion handler like so:

func parseJson(completionHandler: (Bool) -> Void) {
   //do all your json parsing.
   //....
   dispatch_asyc(dispatch_get_main_queue()) {
      //run this if you received the data
      //implement some kind of if statement that checks if the parsing was successful
      completionHandler(true)

      //run this if it failed
      completionHandler(false)
   }
 }

In your viewDidLoad you would do something like

override func viewDidLoad() {
   super.viewDidLoad()
   //...
   parseJson() { success in
      tableView.reloadData()
      if(success) {
         print("success")
      }
   }
}

If you want to display an activity indicator while data is loaded (which I would recommend) it is easier to use a callback as I've just described.

2 Comments

this has an error use of unresolved identifier dispatch_asyc. could you give a more complete code given my current code format ?
I've edited the code a little bit, but basically it should be working. Here is are very nice explanation on why you need dispatch_async: hackingwithswift.com/read/9/4/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.