Search code examples
iosswiftuitableviewswift3uirefreshcontrol

Completion handler in refreshControl has EXC_BAD_ACCESS error


I am using Firebase as my data structure. I use completion handler in my UITableView's refreshControl, in order to stop refreshing when finish loading all data from Firebase.

override func viewDidLoad() {
    super.viewDidLoad()
    self.refreshControl = UIRefreshControl()
    self.refreshControl!.addTarget(self, action: #selector(refreshData),for: .valueChanged)
    self.refreshControl!.attributedTitle = NSAttributedString(string: "Update the data")
    refreshData{ _ in
        self.refreshControl!.endRefreshing()
    }
}

And this is my refreshData method

func refreshData(completionHandler:@escaping (Bool)->() ) {
    //Remove old data
    self.items.removeAll()
    //Renew all data
    var ref: DatabaseReference!
    ref = Database.database().reference(withPath: "tasks")
    //Loading local drafts
    var drafts : [Task]!
    if let local_drafts = NSKeyedUnarchiver.unarchiveObject(withFile: Task.ArchiveURL.path) as? [Task] {
        drafts = local_drafts
    }
    else{
        drafts = []
    }
    //Reloading the database
    ref.observe(.value, with: { snapshot in
        var newItems: [Task] = []
        self.num_of_tasks = Int(snapshot.childrenCount)
        for item in snapshot.children {
            //let local = item as! DataSnapshot
            //let snapshotValue = local.value as! [String: AnyObject]
            //print(snapshotValue["main_content"] as! String!)
            let taskItem = Task(snapshot: item as! DataSnapshot)
            newItems.append(taskItem!)
        }
        let merged = drafts + newItems
        self.items = merged
        completionHandler(true) //THIS LINE HAS ERR_BAD_ACCESS
    })
}

I think the problem might be the two refreshData in viewDidLoad. But I don't know how to fix it. How could I add refreshData with handler as a selector?


Solution

  • This is not a thread problem. I solve it by wrapping the function call in another function because in this line

    self.refreshControl!.addTarget(self, action: #selector(refreshData),for: .valueChanged)
    

    I tried to use refreshData as a selector, but actually, selector doesn't have any completion handler by itself, so that caused the memory error whenever the user tried to renew by drag the scene down. So by wrapping the function call in another function

     func refresh(){
            refreshData{ [weak self] _ in
                if let _ = self {
                    self?.refreshControl!.endRefreshing()
                }
            }
        }
    

    and use this function in the selector, it will deliver the right completion handler, and thus solve the problem.