Извлекать события из EKEventStore и показывать в tableView в Swift iOS8

Я пытаюсь создать небольшое приложение, которое будет извлекать все события и отображать их в виде таблицы.

Мои конкретные вопросы:

  1. Мой fetchEvents() метод реализован правильно?
  2. Где я должен позвонить fetchEvent() метод, чтобы показать список, когда приложение открывается (это в viewDidLoad?) и обновить его после добавления / редактирования нового события?

Спасибо!

Это мой код для MasterViewController.swift файл:

import UIKit
import EventKitUI

class MasterViewController: UITableViewController , EKEventEditViewDelegate{

var objects = NSMutableArray()
let eventStore =  EKEventStore()


override func awakeFromNib() {
    super.awakeFromNib()
}

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view, typically from a nib.
    self.navigationItem.leftBarButtonItem = self.editButtonItem()

    let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
    self.navigationItem.rightBarButtonItem = addButton

    //self.fetchEvents()
}

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

func insertNewObject(sender: AnyObject) {

    let controller = EKEventEditViewController()

    eventStore.requestAccessToEntityType(EKEntityType(), completion: {granted, error in })
    controller.eventStore = eventStore

    controller.editViewDelegate = self
    self.presentModalViewController(controller, animated: true)


    let indexPath = NSIndexPath(forRow: 0, inSection: 0)
    self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}

func eventEditViewController(controller: EKEventEditViewController!, didCompleteWithAction action: EKEventEditViewAction) {
    self.dismissModalViewControllerAnimated(true)
}

func fetchEvents() -> NSMutableArray {

    eventStore.requestAccessToEntityType(EKEntityType(), completion: {granted, error in })

    let endDate = NSDate(timeIntervalSinceNow: 604800*10);   //This is 10 weeks in seconds
    let predicate = self.eventStore.predicateForEventsWithStartDate(NSDate(), endDate: NSDate(), calendars: nil)

    var events = NSMutableArray(array: self.eventStore.eventsMatchingPredicate(predicate))

    return events

    /*
    var indexes = NSMutableIndexSet(index: 5)

    indexes.addIndex(4)

    objects.insertObjects(events, atIndexes: indexes) */

    // Create the start date components



   /* NSDateComponents *oneDayAgoComponents = [[NSDateComponents alloc] init];
    oneDayAgoComponents.day = -1;

    let oneDayAgo = currentCalendar.date dateByAddingComponents:oneDayAgoComponents*/

}

// #pragma mark - Segues

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "showDetail" {
        let indexPath = self.tableView.indexPathForSelectedRow()
        let object = objects[indexPath.row] as NSDate
        (segue.destinationViewController as DetailViewController).detailItem = object
    }
}

// #pragma mark - Table View

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return objects.count
}

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

    let object = objects[indexPath.row] as NSDate
    cell.textLabel.text = object.description
    return cell
}

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    // Return false if you do not want the specified item to be editable.
    return true
}

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == .Delete {
        objects.removeObjectAtIndex(indexPath.row)
        tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
    } else if editingStyle == .Insert {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
    }
}
}

1 ответ

Решение

Прежде всего, вы должны делать остальную часть работы в вашем fetchEvents функция внутри обратного вызова для eventStore.requestAccessToEntityType:

func fetchEvents() -> NSMutableArray {
    eventStore.requestAccessToEntityType(EKEntityType()) completion: {
        granted, error in
        ... the rest of the code ...
    })
}

конечно теперь, когда это асинхронно, вам нужно будет возвращать данные в обратном вызове, а не возвращать их:

func fetchEvents(completed: (NSMutableArray) -> ()) {
    eventStore.requestAccessToEntityType(EKEntityType()) completion: {
        granted, error in
        let endDate = NSDate(timeIntervalSinceNow: 604800*10);   //This is 10 weeks in seconds
        let predicate = self.eventStore.predicateForEventsWithStartDate(NSDate(), endDate: NSDate(), calendars: nil)
        let events = NSMutableArray(array: self.eventStore.eventsMatchingPredicate(predicate))
        completed(events)
    })
}

Но теперь у вас есть другая проблема... вы используете self в замыкании, тем самым захватывая ссылку, что может привести к " циклу ссылок ". В этом конкретном случае я уверен, что этого не произойдет, потому что requestAccessToEntityType() не будет хранить ссылку на ваше закрытие (я уверен), но это хорошая идея для использования [weak self] всякий раз, когда вы используете self внутри функции обратного вызова:

как только вы это сделаете, self является необязательным, так что вам нужно убедиться, что это не ноль…

func fetchEvents(completed: (NSMutableArray) -> ()) {
    eventStore.requestAccessToEntityType(EKEntityType()) completion: { [weak self]
        granted, error in
        if let strongSelf = self {
            let endDate = NSDate(timeIntervalSinceNow: 604800*10);   //This is 10 weeks in seconds
            let predicate = strongSelf.eventStore.predicateForEventsWithStartDate(NSDate(), endDate: NSDate(), calendars: nil)
            let events = NSMutableArray(array: strongSelf.eventStore.eventsMatchingPredicate(predicate))
            completed(events)
        }
    })
}

но, присмотревшись немного ближе, мы никогда не используем self просто self.eventStore так что вместо этого мы можем зафиксировать слабую ссылку на это:

func fetchEvents(completed: (NSMutableArray) -> ()) {
    eventStore.requestAccessToEntityType(EKEntityType()) completion: { [weak weakEventStore = self.eventStore]
        granted, error in
        if let eventStore = weakEventStore {
            let endDate = NSDate(timeIntervalSinceNow: 604800*10);   //This is 10 weeks in seconds
            let predicate = eventStore.predicateForEventsWithStartDate(NSDate(), endDate: NSDate(), calendars: nil)
            let events = NSMutableArray(array: eventStore.eventsMatchingPredicate(predicate))
            completed(events)
        }
    })
}

наконец, вы действительно должны проверять ошибки, а не игнорировать их, (granted может быть ложным, что означает, что пользователь не предоставил вам доступ, или могла произойти ошибка (поэтому необходимо проверить, error ноль (if error { /* handle failure */ })

PS - у меня есть рабочий код, который использует набор событий на GitHub, который вы можете найти полезным.

Другие вопросы по тегам