EventKit no Xamarin.iOS

O iOS tem dois aplicativos relacionados ao calendário integrados: o Aplicativo de Calendário e o Aplicativo de Lembretes. É simples o suficiente para entender como o Aplicativo de Calendário gerencia dados de calendário, mas o Aplicativo de Lembretes é menos óbvio. Os lembretes podem realmente ter datas associadas a eles em termos de quando são devidos, quando são concluídos, etc. Como tal, o iOS armazena todos os dados do calendário, sejam eventos de calendário ou lembretes, em um local, chamado Banco de Dados de Calendário.

A estrutura do EventKit fornece uma maneira de acessar os dados de Calendários, Eventos de Calendário e Lembretes que o Banco de Dados de Calendário armazena. O acesso aos calendários e eventos do calendário está disponível desde o iOS 4, mas o acesso aos lembretes é novo no iOS 6.

Neste guia, abordaremos:

  • Noções básicas do EventKit – Isso introduzirá as peças fundamentais do EventKit por meio das classes principais e fornecerá uma compreensão de seu uso. Esta seção é leitura obrigatória antes de abordar a próxima parte do documento.
  • Tarefas comuns – A seção de tarefas comuns destina-se a ser uma referência rápida sobre como fazer coisas comuns, como enumerar calendários, criar, salvar e recuperar eventos e lembretes de calendário, bem como usar os controladores internos para criar e modificar eventos de calendário. Esta seção não precisa ser lida de frente para trás, pois deve ser uma referência para tarefas específicas.

Todas as tarefas neste guia estão disponíveis no aplicativo de exemplo complementar:

As telas de aplicativo de exemplo complementares

Requisitos

O EventKit foi introduzido no iOS 4.0, mas o acesso aos dados dos Lembretes foi introduzido no iOS 6.0. Como tal, para fazer o desenvolvimento geral do EventKit, você precisará direcionar pelo menos a versão 4.0 e 6.0 para lembretes.

Além disso, o aplicativo Lembretes não está disponível no simulador, o que significa que os dados dos lembretes também não estarão disponíveis, a menos que você os adicione primeiro. Além disso, as solicitações de acesso só são mostradas ao usuário no dispositivo real. Como tal, o desenvolvimento do EventKit é melhor testado no dispositivo.

Noções básicas do kit de eventos

Ao trabalhar com o EventKit, é importante ter uma compreensão das classes comuns e seu uso. Todas essas classes podem ser encontradas no EventKit e EventKitUI (para o EKEventEditController).

EventStore

A classe EventStore é a classe mais importante no EventKit porque é necessária para executar quaisquer operações no EventKit. Ele pode ser considerado como o armazenamento persistente, ou mecanismo de banco de dados, para todos os dados do EventKit. A partir de EventStore você tem acesso aos calendários e eventos de calendário no Aplicativo de Calendário, bem como lembretes no Aplicativo de Lembretes.

Como EventStore é como um mecanismo de banco de dados, ele deve ser de longa duração, o que significa que ele deve ser criado e destruído o mínimo possível durante o tempo de vida de uma instância de aplicativo. Na verdade, é recomendável que, depois de criar uma instância de um EventStore em um aplicativo, você mantenha essa referência por toda a vida útil do aplicativo, a menos que tenha certeza de que não precisará dela novamente. Além disso, todas as chamadas devem ir para uma única EventStore instância. Por esse motivo, o padrão Singleton é recomendado para manter uma única instância disponível.

Criando um repositório de eventos

O código a seguir ilustra uma maneira eficiente de criar uma única instância da EventStore classe e disponibilizá-la estaticamente de dentro de um aplicativo:

public class App
{
    public static App Current {
            get { return current; }
    }
    private static App current;

    public EKEventStore EventStore {
            get { return eventStore; }
    }
    protected EKEventStore eventStore;

    static App ()
    {
            current = new App();
    }
    protected App () 
    {
            eventStore = new EKEventStore ( );
    }
}

O código acima usa o padrão Singleton para instanciar uma instância do EventStore quando o aplicativo é carregado. O EventStore pode então ser acessado globalmente de dentro do aplicativo da seguinte maneira:

App.Current.EventStore;

Observe que todos os exemplos aqui usam esse padrão, então eles fazem referência à EventStore via App.Current.EventStore.

Solicitando acesso a dados de calendário e lembrete

Antes de ter permissão para acessar quaisquer dados por meio do EventStore, um aplicativo deve primeiro solicitar acesso aos dados de eventos do calendário ou aos dados de lembretes, dependendo de qual deles você precisa. Para facilitar isso, o EventStore expõe um método chamado RequestAccess que, quando chamado, mostrará uma exibição de alerta para o usuário informando que o aplicativo está solicitando acesso aos dados do calendário ou aos dados do lembrete, dependendo do que EKEntityType é passado para ele. Como gera uma exibição de alerta, a chamada é assíncrona e chamará um manipulador de conclusão passado como um NSAction (ou Lambda) para ele, que receberá dois parâmetros: um booleano de se o acesso foi concedido ou não e um NSError, que, se não-nulo, conterá qualquer informação de erro na solicitação. Por exemplo, o código a seguir solicitará acesso aos dados de eventos do calendário e mostrará uma exibição de alerta se a solicitação não tiver sido concedida.

App.Current.EventStore.RequestAccess (EKEntityType.Event, 
    (bool granted, NSError e) => {
            if (granted)
                    //do something here
            else
                    new UIAlertView ( "Access Denied", 
"User Denied Access to Calendar Data", null,
"ok", null).Show ();
            } );

Uma vez que a solicitação tenha sido concedida, ela será lembrada desde que o aplicativo esteja instalado no dispositivo e não aparecerá um alerta para o usuário. No entanto, o acesso é dado apenas ao tipo de recurso, eventos de calendário ou lembretes concedidos. Se um aplicativo precisar de acesso a ambos, ele deve solicitar ambos.

Como a permissão é lembrada, é relativamente barato fazer a solicitação todas as vezes, por isso é uma boa ideia sempre solicitar acesso antes de executar uma operação.

Além disso, como o manipulador de conclusão é chamado em um thread separado (não-interface do usuário), quaisquer atualizações para a interface do usuário no manipulador de conclusão devem ser chamadas via InvokeOnMainThread, caso contrário, uma exceção será lançada e, se não for capturada, o aplicativo falhará.

EKEntityType

EKEntityType é uma enumeração que descreve o tipo de EventKit item ou dados. Ele tem dois valores: Event e Lembrete. Ele é usado em vários métodos, inclusive EventStore.RequestAccess para dizer EventKit que tipo de dados obter acesso ou recuperar.

EKCalendar

EKCalendar representa um calendário, que contém um grupo de eventos de calendário. Os calendários podem ser armazenados em muitos lugares diferentes, como localmente, no iCloud, em um local de provedor de terceiros 3rd, como um Exchange Server ou Google, etc. Muitas vezes EKCalendar é usado para dizer EventKit onde procurar eventos, ou onde salvá-los.

EKEventEditController

EKEventEditController pode ser encontrado no EventKitUI namespace e é um controlador interno que pode ser usado para editar ou criar eventos de calendário. Muito parecido com os controladores de câmera integrados, EKEventEditController faz o trabalho pesado para você na exibição da interface do usuário e manipulação de economia.

EKEvent

EKEvent representa um evento de calendário. Ambos EKEvent e EKReminder herdam de EKCalendarItem e têm campos como Title, Notese assim por diante.

EKReminder

EKReminder representa um item de lembrete.

EKSpan

EKSpan é uma enumeração que descreve a extensão de eventos ao modificar eventos que podem ocorrer novamente e tem dois valores: ThisEvent e FutureEvents. ThisEvent significa que quaisquer alterações ocorrerão apenas no evento específico da série que está sendo referenciada, enquanto FutureEvents afetarão esse evento e todas as recorrências futuras.

Tarefas

Para facilitar o uso, o uso do EventKit foi dividido em tarefas comuns, descritas nas seções a seguir.

Enumerar calendários

Para enumerar os calendários que o usuário configurou no dispositivo, chame GetCalendars o EventStore e passe o tipo de calendários (lembretes ou eventos) que você deseja receber:

EKCalendar[] calendars = 
App.Current.EventStore.GetCalendars ( EKEntityType.Event );

Adicionar ou modificar um evento usando o controlador interno

O EKEventEditViewController faz muito do trabalho pesado para você se você quiser criar ou editar um evento com a mesma interface do usuário que é apresentada ao usuário ao usar o aplicativo de calendário:

A interface do usuário que é apresentada ao usuário ao usar o aplicativo de calendário

Para usá-lo, convém declará-lo como uma variável de nível de classe para que ele não seja coletado de lixo se for declarado dentro de um método:

public class HomeController : DialogViewController
{
        protected CreateEventEditViewDelegate eventControllerDelegate;
        ...
}

Em seguida, para iniciá-lo: instancie-o, dê-lhe uma referência ao EventStore, conecte um delegado EKEventEditViewDelegate a ele e, em seguida, exiba-o usando PresentViewController:

EventKitUI.EKEventEditViewController eventController = 
        new EventKitUI.EKEventEditViewController ();

// set the controller's event store - it needs to know where/how to save the event
eventController.EventStore = App.Current.EventStore;

// wire up a delegate to handle events from the controller
eventControllerDelegate = new CreateEventEditViewDelegate ( eventController );
eventController.EditViewDelegate = eventControllerDelegate;

// show the event controller
PresentViewController ( eventController, true, null );

Opcionalmente, se você quiser preencher previamente o evento, poderá criar um novo evento (conforme mostrado abaixo) ou recuperar um evento salvo:

EKEvent newEvent = EKEvent.FromStore ( App.Current.EventStore );
// set the alarm for 10 minutes from now
newEvent.AddAlarm ( EKAlarm.FromDate ( DateTime.Now.AddMinutes ( 10 ) ) );
// make the event start 20 minutes from now and last 30 minutes
newEvent.StartDate = DateTime.Now.AddMinutes ( 20 );
newEvent.EndDate = DateTime.Now.AddMinutes ( 50 );
newEvent.Title = "Get outside and exercise!";
newEvent.Notes = "This is your reminder to go and exercise for 30 minutes.”;

Se você deseja preencher previamente a interface do usuário, certifique-se de definir a propriedade Event no controlador:

eventController.Event = newEvent;

Para usar um evento existente, consulte a seção Recuperar um evento por ID mais adiante.

O delegado deve substituir o Completed método, que é chamado pelo controlador quando o usuário termina de usar a caixa de diálogo:

protected class CreateEventEditViewDelegate : EventKitUI.EKEventEditViewDelegate
{
        // we need to keep a reference to the controller so we can dismiss it
        protected EventKitUI.EKEventEditViewController eventController;

        public CreateEventEditViewDelegate (EventKitUI.EKEventEditViewController eventController)
        {
                // save our controller reference
                this.eventController = eventController;
        }

        // completed is called when a user eith
        public override void Completed (EventKitUI.EKEventEditViewController controller, EKEventEditViewAction action)
        {
                eventController.DismissViewController (true, null);
                }
        }
}

Opcionalmente, no delegado, você pode marcar a Completed Ação no método para modificar o evento e salvar novamente, ou fazer outras coisas, se for cancelado, etc:

public override void Completed (EventKitUI.EKEventEditViewController controller, EKEventEditViewAction action)
{
        eventController.DismissViewController (true, null);

        switch ( action ) {

        case EKEventEditViewAction.Canceled:
                break;
        case EKEventEditViewAction.Deleted:
                break;
        case EKEventEditViewAction.Saved:
                // if you wanted to modify the event you could do so here,
// and then save:
                //App.Current.EventStore.SaveEvent ( controller.Event, )
                break;
        }
}

Criando um evento programaticamente

Para criar um evento no código, use o método de fábrica FromStore na EKEvent classe e defina todos os dados nela:

EKEvent newEvent = EKEvent.FromStore ( App.Current.EventStore );
// set the alarm for 10 minutes from now
newEvent.AddAlarm ( EKAlarm.FromDate ( DateTime.Now.AddMinutes ( 10 ) ) );
// make the event start 20 minutes from now and last 30 minutes
newEvent.StartDate = DateTime.Now.AddMinutes ( 20 );
newEvent.EndDate = DateTime.Now.AddMinutes ( 50 );
newEvent.Title = "Get outside and do some exercise!";
newEvent.Notes = "This is your motivational event to go and do 30 minutes of exercise. Super important. Do this.";

Você deve definir o calendário no qual deseja salvar o evento, mas se não tiver preferência, poderá usar o padrão:

newEvent.Calendar = App.Current.EventStore.DefaultCalendarForNewEvents;

Para salvar o evento, chame EventStoreo método SaveEvent no :

NSError e;
App.Current.EventStore.SaveEvent ( newEvent, EKSpan.ThisEvent, out e );

Depois de salva, a propriedade EventIdentifier será atualizada com um identificador exclusivo que pode ser usado posteriormente para recuperar o evento:

Console.WriteLine ("Event Saved, ID: " + newEvent.CalendarItemIdentifier);

EventIdentifier é um GUID formatado em cadeia de caracteres.

Criar um lembrete programaticamente

Criar um lembrete no código é o mesmo que criar um evento de calendário:

EKReminder reminder = EKReminder.Create ( App.Current.EventStore );
reminder.Title = "Do something awesome!";
reminder.Calendar = App.Current.EventStore.DefaultCalendarForNewReminders;

Para salvar, chame o método SaveReminder no EventStore:

NSError e;
App.Current.EventStore.SaveReminder ( reminder, true, out e );

Recuperando um evento por ID

Para recuperar um evento por sua ID, use o método EventFromIdentifier no EventStore e passe-o para o EventIdentifier que foi retirado do evento:

EKEvent mySavedEvent = App.Current.EventStore.EventFromIdentifier ( newEvent.EventIdentifier );

Para eventos, há duas outras propriedades de identificador, mas EventIdentifier é a única que funciona para isso.

Recuperando um lembrete por ID

Para recuperar um lembrete, use o método GetCalendarItem no EventStore e passe-lhe o CalendarItemIdentifier:

EKCalendarItem myReminder = App.Current.EventStore.GetCalendarItem ( reminder.CalendarItemIdentifier );

Porque GetCalendarItem retorna um EKCalendarItem, ele deve ser convertido para EKReminder se você precisar acessar dados de lembrete ou usar a instância como um EKReminder posterior.

Não use GetCalendarItem para eventos do calendário, pois no momento em que escrevo, ele não funciona.

Excluindo um evento

Para excluir um evento de calendário, chame RemoveEvent em seu EventStore e passe uma referência ao evento e o apropriado EKSpan:

NSError e;
App.Current.EventStore.RemoveEvent ( mySavedEvent, EKSpan.ThisEvent, true, out e);

No entanto, depois que um evento for excluído, a referência do evento será null.

Excluindo um lembrete

Para excluir um lembrete, chame EventStore RemoveReminder no e passe uma referência para o lembrete:

NSError e;
App.Current.EventStore.RemoveReminder ( myReminder as EKReminder, true, out e);

Observe que no código acima há um cast para EKReminder, porque GetCalendarItem foi usado para recuperá-lo

Procurando Eventos

Para procurar eventos de calendário, você deve criar um objeto NSPredicate por meio do método PredicateForEvents no EventStore. An NSPredicate é um objeto de dados de consulta que o iOS usa para localizar correspondências:

DateTime startDate = DateTime.Now.AddDays ( -7 );
DateTime endDate = DateTime.Now;
// the third parameter is calendars we want to look in, to use all calendars, we pass null
NSPredicate query = App.Current.EventStore.PredicateForEvents ( startDate, endDate, null );

Depois de criar o NSPredicate, use o método EventsMatching no EventStore:

// execute the query
EKCalendarItem[] events = App.Current.EventStore.EventsMatching ( query );

Observe que as consultas são síncronas (bloqueio) e podem levar muito tempo, dependendo da consulta, portanto, convém criar um novo thread ou tarefa para fazê-lo.

Procurando lembretes

A busca por lembretes é semelhante a eventos; Ele requer um predicado, mas a chamada já é assíncrona, então você não precisa se preocupar em bloquear o thread:

// create our NSPredicate which we'll use for the query
NSPredicate query = App.Current.EventStore.PredicateForReminders ( null );

// execute the query
App.Current.EventStore.FetchReminders (
        query, ( EKReminder[] items ) => {
                // do someting with the items
        } );

Resumo

Este documento forneceu uma visão geral das partes importantes da estrutura do EventKit e de várias das tarefas mais comuns. No entanto, a estrutura do EventKit é muito grande e poderosa, e inclui recursos que não foram introduzidos aqui, como: atualizações em lote, configuração de alarmes, configuração de recorrência em eventos, registro e escuta de alterações no banco de dados de calendário, configuração de GeoFences e muito mais. Para obter mais informações, consulte o Guia de Programação de Calendário e Lembretes da Apple.