3

I've got JSON filling my UITableView successfully, but the JSON is often updated so I need the ability to refresh. I followed THIS TUTORIAL to implement a pull to refresh control. Visually, it seems like it all works correctly, but when I call tableView.reloadData() the table doesn't reload. However, if I leave the ViewController and return, the table is updated. Why would tableView.reloadData() work in viewDidAppear and viewWillAppear but not in my custom refresh() function?

MainVC.swift file

    class MainVC: UIViewController, UITableViewDelegate, UITableViewDataSource {

        @IBOutlet var tableView: UITableView!
        var dataArray: NSArray = NSArray()

        @IBOutlet var Controller: UISegmentedControl!

        var refreshControl:UIRefreshControl!

        func refresh(sender:AnyObject)
        {
            refreshBegin("Refresh",
                refreshEnd: {(x:Int) -> () in
                    self.tableView .reloadData()
                    println("Table Reloaded")
                    self.refreshControl.endRefreshing()
            })
        }

        func refreshBegin(newtext:String, refreshEnd:(Int) -> ()) {
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
                println("refreshing")
                sleep(2)

                dispatch_async(dispatch_get_main_queue()) {
                    refreshEnd(0)
                }
            }
        }

        override func viewWillAppear(animated: Bool) {
            self.tableView .reloadData()
        }

        override func viewDidLoad() {
            super.viewDidLoad()
            navigationItem.titleView = UIImageView(image: UIImage(named: "logojpg.jpg"))
            startConnectionAt("http://www.domain.com/json.php")


            refreshControl = UIRefreshControl()
            refreshControl.backgroundColor = UIColor.orangeColor()
            refreshControl.tintColor = UIColor.whiteColor()
            refreshControl.attributedTitle = NSAttributedString(string: "Pull to Refresh")
            refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
            tableView.addSubview(refreshControl)

            }

//MARK: JSON Loading

    var data: NSMutableData = NSMutableData()
    func startConnectionAt(urlPath: String){
        var url: NSURL = NSURL(string: urlPath)
        var request: NSURLRequest = NSURLRequest(URL: url)
        var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)
        connection.start()
    }

    func connection(connection: NSURLConnection!, didFailWithError error: NSError!) {
        println("Connection failed.\(error.localizedDescription)")
    }

    func connection(connection: NSURLConnection, didRecieveResponse response: NSURLResponse)  {
        println("Recieved response")
    }

    func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
        self.data = NSMutableData()
    }

    func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
        self.data.appendData(data)
    }

    func connectionDidFinishLoading(connection: NSURLConnection!) {
        var dataAsString: NSString = NSString(data: self.data, encoding: NSUTF8StringEncoding)
        var err: NSError
        var json: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
        var results: NSArray = json["needs"] as NSArray
        self.dataArray = results

        tableView.reloadData()
        println("success")
    }

//End loading of JSON

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
        return self.dataArray.count;
    }

    func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
        var cell:CustomCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as CustomCell

        var rowData: NSDictionary = dataArray[indexPath.row] as NSDictionary
        var firstName=rowData["needFirstname"] as String
        var descrip=rowData["needDescription"] as String
        var poster=rowData["needPoster"] as String
        var city=rowData["needCity"] as String
        var state=rowData["needState"] as String
        var country=rowData["needCountry"] as String

        cell.needFirstName.text = firstName
        cell.needDescription.text = descrip
        cell.needDescription.numberOfLines = 0
        cell.needPoster.text = poster
        cell.needCity.text = city
        cell.needState.text = state
        cell.needCountry.text = country

        return cell
    }


    @IBAction func Change(sender: AnyObject) {

        if Controller.selectedSegmentIndex == 0 {
            startConnectionAt("http://www.domain.com/localJSON.php")
        }
        else if Controller.selectedSegmentIndex == 1 {
            startConnectionAt("http://www.domain.com/intlJSON.php")
        }

        self.tableView .reloadData()

    }

}
Community
  • 1
  • 1
davidrayowens
  • 1,562
  • 2
  • 13
  • 23
  • 1
    Hey David - I just tried to replicate a simple example without web service calls and mine worked. Where is it that you're setting dataArray to a new NSArray? – andrewcbancroft Aug 29 '14 at 16:52
  • I'll add the rest of my MainVC file – davidrayowens Aug 29 '14 at 16:54
  • To answer you're question Andrew, the dataArray is pushed into a new NSArray "results" in the connectionDidFinishLoading function – davidrayowens Aug 29 '14 at 17:26
  • I see `self.dataArray = results` and `tableView.reloadData()` in your `connectionDidFinishLoading` method. However, I don't see where you actually make another call out to your service to let this callback method actually get executed again during your manual pull to refresh process. The `refresh` function simply calls `tableView.reloadData()`, but since there's nothing new in the array (because no web service has been called), nothing get's reloaded... unless I'm missing something else in your approach... – andrewcbancroft Aug 29 '14 at 17:47
  • So does that I mean I need to push results yet again into a new NSArray inside of my refresh function? – davidrayowens Aug 29 '14 at 17:54

2 Answers2

5

Your last comment is right-on in my view.

During your pull to refresh function, you call tableView.reloadData(), however, reloadData() does not inherently do any repopulating the elements in the data source (in your case, dataArray). It simply reloads all the data that's currently in the table view's data source at the time it is called.

So my recommendation would be to construct your refresh function such that the following happens:

  1. Initiate a request to your web service.
  2. When the response comes back (ie, connectionDidFinishLoading is executed), parse the JSON results and assign that result to the dataArray instance. You seem to be doing this already in connectionDidFinishLoading, so it's just a matter of sending the request to your web service, I'd think.
  3. Call tableView.reloadData() to display any new elements that have been added since the last time the tableView's data was displayed. Again, you're doing this already in connectionDidFinishLoading, so #1 is the primary thing that I think needs to happen.
andrewcbancroft
  • 941
  • 1
  • 9
  • 11
4

Referring to https://stackoverflow.com/a/25957339

Not sure but maybe the connection is run on a different thread, if so you need to run the table update on the main UI thread

// using Swift's trailing closure syntax:
dispatch_async(dispatch_get_main_queue()) {
    self.tableView.reloadData()
}
Community
  • 1
  • 1
  • Reload table in mainThread in swift http://stackoverflow.com/questions/24126261/swift-alternative-to-performselectoronmainthread/31853168#31853168 – Anand Suthar Aug 06 '15 at 10:46
  • It needs a parentesis :) dispatch_async(dispatch_get_main_queue()) { self.tableView.reloadData() } – Dx_ Aug 31 '15 at 00:59