jeffery_the_wind
jeffery_the_wind

Reputation: 18188

Having a hard time reloading UITableView

I am working with the following class which successfully loads a row to the UITableView.

import UIKit
import ResearchKit

enum Activity: Int {
    case Demographics

    static var allValues: [Activity] {
        var idx = 0
        return Array(anyGenerator{ return self.init(rawValue: idx++)})
    }

    var title: String {
        switch self {
        case .Demographics:
            return "Demographics"
        }
    }

    var subtitle: String {
        switch self {
            case .Demographics:
                return "Demographics Survey"
        }
    }
}

class ActivityViewController: UITableViewController {

    // MARK: UITableViewDataSource

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        guard section == 0 else { return 0 }

        return Activity.allValues.count
    }

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

        if let activity = Activity(rawValue: indexPath.row) {
            cell.textLabel?.text = activity.title
            cell.detailTextLabel?.text = activity.subtitle
            if (activity.title == "Demographics"){
                if (Data().is_demo_complete()){
                    cell.textLabel?.textColor = UIColor.lightGrayColor()
                    cell.detailTextLabel?.textColor = UIColor.lightGrayColor()
                    cell.userInteractionEnabled = false
                }
            }
        }

        return cell
    }

    func reloadtable(){
        self.tableView.reloadData()
    }

    // MARK: UITableViewDelegate

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        guard let activity = Activity(rawValue: indexPath.row) else { return }

        let taskViewController: ORKTaskViewController
        switch activity {
            case .Demographics:
                taskViewController = ORKTaskViewController(task: DemoTask, taskRunUUID: NSUUID())


        }

        taskViewController.delegate = self
        navigationController?.presentViewController(taskViewController, animated: true, completion: nil)
    }
}

I have another class called Data where I store all communications to my server. The idea is that I have to check some data on the server to know whether or not to grey out and disable one of the rows in the tableView. From the data class, when the server call is completed and successful, i do this:

dispatch_async(dispatch_get_main_queue(), { () -> Void in
    ActivityViewController().reloadtable()
})

I have confirmed, that successfully calls the reloadtable() function, in which it runs self.tableView.reloadData(). The place I am stuck is that after that, the tableView doesn't actually reload. I can tell because I put a breakpoint on the line that says if let activity = Activity(rawValue: indexPath.row) { and the breakpoint does not get triggered a second time, even though I can confirm that the reloadtable() function is indeed triggered. What am I doing wrong here, why isn't the table being reloaded? Thanks!

EDIT

Here is the Data class:

class Data: NSObject, NSURLSessionDelegate {

    var unique_id = UIDevice.currentDevice().identifierForVendor!.UUIDString;

    var demo_complete = false

    func check_demo_complete(){

        let request = NSMutableURLRequest(URL: NSURL(string: "https://www.myurl.com/is_demo_complete.php")!)
        request.HTTPMethod = "POST"
        let postString = "unique_id=\(unique_id)&pass=somepass"
        request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)

        let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
        let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)

        let task = session.dataTaskWithRequest(request) {
            data, response, error in

            if error != nil {
                print("error=\(error)")
                return
            }

            let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)

            if ( responseString == "true" ) {
                self.demo_complete = true
                print ("Demo has been completed")
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    ActivityViewController().reloadtable()
                })
            }
        }
        task.resume()
    }

    func is_demo_complete() -> Bool{
        return self.demo_complete
    }


}

Upvotes: 0

Views: 101

Answers (1)

aimak
aimak

Reputation: 540

The problem is the ActivityViewController().reloadtable() call. This creates a new ActivityViewController each time your Data class downloaded data. It is important that you'd only call reloadtable()on the same ActivityViewController instance that already exists (and that is displayed on screen).

One good (basic but simple) solution would be to do the following refactoring :

func check_demo_complete(completion:(Bool -> ()){
  // your network call
  dispatch_async(dispatch_get_main_queue()) {
    if ( responseString == "true" ) {
      completion(true)
    }
    else {
      completion(false)
    }
    // or, simpler : completion(responseString == "true")
  }
}

That way, by calling check_demo_complete(), you'd be obligated to pass a closure :

if (activity.title == "Demographics"){
  Data().check_demo_complete() { [weak self] success in
     if success {
       // change your textColors etc
       self?.tableView.reloadData()
     }
  }
}

Again, this is a basic solution that should work. The important lesson to learn is : don't create new instance of classes each time you need them.

Upvotes: 1

Related Questions