Search code examples
swiftswift3remindersekeventstore

swift 3 numberofrowsinsection runs before viewwillappear fetches reminders


Would like to ask for some help in retrieving reminders. Seems like numberOfRowsInSection and is getting called even before eventStore.fetchReminders has finished. Here is my code:

override func viewWillAppear(_ animated: Bool) {
    print("****viewWillAppear")
    super.viewWillAppear(true)
    self.eventStore = EKEventStore()
    self.reminders = [EKReminder]()
    //Added to initialize
    self.reminders.removeAll()
    self.eventStore!.requestAccess(to: EKEntityType.reminder, completion:
        {(granted, error) in
            if granted
            {
                let predicate = self.eventStore.predicateForReminders(in: nil)
                self.eventStore.fetchReminders(matching: predicate, completion: { (reminders: [EKReminder]?) -> Void in
                    self.reminders = reminders
                    DispatchQueue.main.async() {
                        //Added 
                        self.reminders = reminders
                        self.tableView.reloadData()
                    }
                })

            }
            else 
            {
                print("The app is not permitted to access reminders, make sure to grant permission in the settings and try again")
            }
    })

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

    print("*****numberOfRowsInSection")
    print("numberOfRowsInSection reminders \(self.reminders.count)")
    return self.reminders.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    print("****titleForHeaderInSection")
    print("titleForHeaderInSection reminders \(self.reminders.count)")
    return "test"
}

I tried putting it in viewDidLoad but same thing happens, the self.reminders.count in numberofRowsInSection is returning 0. I also tried to call fetchReminders OUTSIDE of requestAccess (because I thought requestAccess was not firing), but I end up with the same thing. Is there anything I'm doing wrong? Would really appreciate any advise!

Thanks!


Solution

  • This is normal. The UITableViewController attempts to display the contents of the table view as the view controller is being shown. You should initialize self.reminders with an empty array at the start of viewDidLoad so the table view initially shows no results.

    Then, once your access of the event store is complete, your code to reset self.reminders and reload the table view will be called.

    One critical change you should make is to set self.reminders inside the DispatchQueue.main.async() block. This ensures the data source is updated just before the table is reloaded and not before hand. In other words, update your code to:

    self.eventStore.fetchReminders(matching: predicate, completion: { (reminders: [EKReminder]?) -> Void in
        DispatchQueue.main.async() {
            self.reminders = reminders
            self.tableView.reloadData()
        }
    })
    

    One other change to consider is to initially set self.reminders to nil and have your table view data source methods treat this as a signal to show one row that says "loading" (or something similar). Otherwise your users may wonder why they are starring at a mostly blank screen for a brief period of time.