Async-
Async-

Reputation: 3289

Building table view while getting number of rows from completion block

I am trying to retrieve the calendar events in swift 2, and I can not solve this: to build the table view I need to know the number of cells, which I can get from a method like this (for the sake of simplicity array is String):

func fetchCalendarEvents () -> [String] {

    var arrayW = [String]()

    let eventStore : EKEventStore = EKEventStore()


    eventStore.requestAccessToEntityType(EKEntityType.Event, completion: {
        granted, error in
        if (granted) && (error == nil) {
            print("access granted: \(granted)")

            //do stuff...
        }
        else {
            print("error: access not granted  \(error)")

        }
    })
    return arrayW
}

I am calling this method in viewDidLoad, and save the array to var eventArray. Immediately the following method gets called:

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

    return eventArray.count
}

The problem is that the completion block of the fetchCalendarEvents is not complete at this point, and therefore returns 0 (while there are events in the calendar).

My question: how can I handle building the table view from array that I get from method, that has completion block, and takes some time to be completed?

Upvotes: 2

Views: 123

Answers (4)

Abhinav
Abhinav

Reputation: 38162

Add aBlockSelf.tableView.reloadData() in your calendar completion block to reload your table with fetched data.

Here aBlockSelf is a weak reference to self because its being passed to a block.

EDIT: Post OP comment - Try this:

weak var aBlockSelf = self

Upvotes: 2

Async-
Async-

Reputation: 3289

Ok, all seemed to answer relatively correct, but not exactly in the way that worked straight. What worked for me, is that instead of returning array, I just need to reload the table view after the for loop, where array is built up:

func fetchCalendarEvents () {

    let eventStore : EKEventStore = EKEventStore()

    eventStore.requestAccessToEntityType(EKEntityType.Event, completion: {
        granted, error in
        if (granted) && (error == nil) {
            print("access granted: \(granted)")
            let startDate=NSDate().dateByAddingTimeInterval(-60*60*24)
            let endDate=NSDate().dateByAddingTimeInterval(60*60*24*3)
            let predicate2 = eventStore.predicateForEventsWithStartDate(startDate, endDate: endDate, calendars: nil)

            print("startDate:\(startDate) endDate:\(endDate)")
            let events = eventStore.eventsMatchingPredicate(predicate2) as [EKEvent]!

            if events != nil {

                var arrayOfEvents = [CalendarEventObject]()

                for event in events {
                    var eventObject: CalendarEventObject

                    //  (ಠ_ಠ)  HARDCODEDE
                    eventObject = CalendarEventObject(id: 0, title: event.title, location: event.location!, notes: event.notes!, startTime: event.startDate, endTime: event.endDate, host: "host", origin: "origin", numbers: ["0611111111", "0611111112"], passcodes: ["123456", "123457"], hostcodes: ["123458", "123459"], selectedNumber: "0611111113", selectedPasscode: "123457", selectedHostcode: "123459", scheduled: true, parsed: false, update: true, preferences: [], eventUrl: "www.youtube.com", attendees: [])

                    arrayOfEvents.append(eventObject)

                }

                self.eventArray = arrayOfEvents

                //reload data after getting the array
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    self.tableView.reloadData()
                })
            }
        }
        else {
            print("error: access not granted  \(error)")

        }
    })
}

Upvotes: 0

Anton Onizhuk
Anton Onizhuk

Reputation: 96

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if (eventArray.count == 0) { //or unwrap value, depends on your code
        return 0 // or 1, if you want add a 'Loading' cell
    } else {
        return eventArray.count
    }    
}

And, when you get eventArray, just reload table with

//without animation
tableView.reloadData()
//or with, but it can get little tricky
tableView.insertRowsAtIndexPaths(indexArray, withRowAnimation:UITableViewRowAnimationRight];

Upvotes: 1

user3820674
user3820674

Reputation: 262

Make 2 cells type. One for events and one which just say "Loading...". While your block in progress show only 1 cell with "Loading...". When an events will retrieve hide this cell and reload table with events.
Cheers.

Upvotes: 0

Related Questions