CloudKit no Xamarin.iOS

A estrutura do CloudKit simplifica o desenvolvimento de aplicativos que acessam o iCloud. Isso inclui a recuperação de dados do aplicativo e direitos de ativos, bem como a capacidade de armazenar com segurança as informações do aplicativo. Esse kit oferece aos usuários uma camada de anonimato, permitindo acesso a aplicativos com suas IDs do iCloud sem compartilhar informações pessoais.

Os desenvolvedores podem se concentrar em seus aplicativos do lado do cliente e permitir que o iCloud elimine a necessidade de gravar a lógica do aplicativo do lado do servidor. O CloudKit fornece autenticação, bancos de dados públicos e privados e serviços estruturados de armazenamento de ativos e dados estruturados.

Importante

A Apple fornece ferramentas para ajudar os desenvolvedores a lidar adequadamente com o GDPR (Regulamento Geral sobre a Proteção de Dados) da União Europeia.

Requisitos

O seguinte é necessário para concluir as etapas apresentadas neste artigo:

  • Xcode e o SDK do iOS – as APIs Xcode e iOS 8 da Apple precisam ser instaladas e configuradas no computador do desenvolvedor.
  • Visual Studio para Mac – a versão mais recente do Visual Studio para Mac deve ser instalada e configurada no dispositivo de usuário.
  • Dispositivo iOS 8 – um dispositivo iOS que executa a versão mais recente do iOS 8 para teste.

O que é o CloudKit?

O CloudKit é uma maneira de dar ao desenvolvedor acesso aos servidores iCloud. Ele fornece a base para iCloud Drive e iCloud Photo Library. O CloudKit tem suporte em dispositivos macOS e iOS.

Como o CloudKit tem suporte em dispositivos macOS e iOS

O CloudKit usa a infraestrutura da conta do iCloud. Se houver um usuário conectado a uma conta do iCloud no dispositivo, o CloudKit usará sua ID para identificar o usuário. Se nenhuma conta estiver disponível, o acesso somente leitura limitado será fornecido.

O CloudKit dá suporte ao conceito de bancos de dados públicos e privados. Os bancos de dados públicos fornecem uma "sopa" de todos os dados aos quais o usuário tem acesso. Os bancos de dados privados destinam-se a armazenar os dados privados associados a um usuário específico.

O CloudKit dá suporte a dados estruturados e em massa. Ele é capaz de lidar com grandes transferências de arquivos perfeitamente. O CloudKit cuida da transferência eficiente de arquivos grandes de e para os servidores iCloud em segundo plano, liberando o desenvolvedor para se concentrar em outras tarefas.

Observação

É importante observar que o CloudKit é uma tecnologia de transporte. Ele não fornece persistência; ele só permite que um aplicativo envie e receba informações dos servidores com eficiência.

A partir desta escrita, a Apple está inicialmente fornecendo o CloudKit gratuitamente com um alto limite de largura de banda e capacidade de armazenamento. Para projetos ou aplicativos maiores com uma base de usuários grande, a Apple insinuou que uma escala de preços acessível será fornecida.

Habilitando o CloudKit em um aplicativo Xamarin

Antes que um aplicativo Xamarin possa utilizar a estrutura do CloudKit, o aplicativo deve ser provisionado corretamente conforme detalhado nos guias Trabalhando com Funcionalidades e Trabalhando com Direitos .

Para acessar o CloudKit, o arquivo Entitlements.plist deve incluir permissões Habilitar iCloud, Armazenamento de chave-valor e CloudKit .

Aplicativo de exemplo

O exemplo CloudKitAtlas demonstra como usar o CloudKit com o Xamarin. As etapas abaixo mostram como configurar o exemplo – ele requer configurações adicionais além do que é necessário apenas para o CloudKit:

  1. Abra o projeto no Visual Studio para Mac ou no Visual Studio.
  2. No Gerenciador de Soluções, abra o arquivo Info.plist e verifique se o Identificador de Pacote corresponde ao que foi definido na ID do Aplicativo criada como parte da configuração de provisionamento.
  3. Role para baixo até a parte inferior do arquivo Info.plist e selecione Modos de Plano de Fundo Habilitados, Atualizações local e Notificações Remotas.
  4. Clique com o botão direito do mouse no projeto do iOS na solução e selecione Opções.
  5. Selecione Assinatura de Pacote do iOS, selecione o Perfil de Provisionamento e Identidade do Desenvolvedor criado acima.
  6. Verifique se o Entitlements.plist inclui Habilitar iCloud, Armazenamento de chave-valor e CloudKit.
  7. Verifique se o contêiner Ubiquity existe para o aplicativo. Exemplo: iCloud.com.your-company.CloudKitAtlas
  8. Salve as alterações no arquivo.

Com essas configurações em vigor, o aplicativo de exemplo agora está pronto para acessar as APIs do CloudKit Framework, bem como serviços de segundo plano, localização e notificação.

Visão geral da API do CloudKit

Antes de implementar o CloudKit em um aplicativo Xamarin iOS, este artigo abordará os conceitos básicos do CloudKit Framework, que incluirá os seguintes tópicos:

  1. Contêineres – silos isolados de comunicações do iCloud.
  2. Bancos de dados – públicos e privados estão disponíveis para o aplicativo.
  3. Registros – o mecanismo no qual os dados estruturados são movidos de e para o CloudKit.
  4. Zonas de Registro – são grupos de registros.
  5. Identificadores de registro – são totalmente normalizados e representam o local específico do registro.
  6. Referência – forneça relações pai-filho entre registros relacionados em um determinado banco de dados.
  7. Ativos – permitir que o arquivo de dados grandes e não estruturados seja carregado no iCloud e associado a um determinado Registro.

Contêineres

Um determinado aplicativo em execução em um dispositivo iOS está sempre em execução junto com outros aplicativos e serviços nesse dispositivo. No dispositivo cliente, o aplicativo será siloed ou em área restrita de alguma forma. Em alguns casos, essa é uma área restrita literal e, em outros, o aplicativo está simplesmente em execução em seu próprio espaço de memória.

O conceito de pegar um aplicativo cliente e executá-lo separado de outros clientes é muito poderoso e fornece as seguintes vantagens:

  1. Segurança – um aplicativo não pode interferir com outros aplicativos cliente ou com o próprio sistema operacional.
  2. Estabilidade – se o aplicativo cliente falhar, ele não poderá eliminar outros aplicativos do sistema operacional.
  3. Privacidade – cada aplicativo cliente tem acesso limitado às informações pessoais armazenadas no dispositivo.

O CloudKit foi projetado para fornecer as mesmas vantagens que as listadas acima e aplicá-las ao trabalho com informações baseadas em nuvem:

Aplicativos cloudkit se comunicam usando contêineres

Assim como o aplicativo que está sendo um-de-muitos em execução no dispositivo, assim como as comunicações do aplicativo com o iCloud um-de-muitos. Cada um desses diferentes silos de comunicação são chamados contêineres.

Os contêineres são expostos no CloudKit Framework por meio da CKContainer classe . Por padrão, um aplicativo fala com um contêiner e esse contêiner separa os dados desse aplicativo. Isso significa que vários aplicativos podem armazenar informações para a mesma conta do iCloud, mas essas informações nunca serão intercaladas.

A conteinerização de dados do iCloud também permite que o CloudKit encapsule as informações do usuário. Dessa forma, o aplicativo terá acesso limitado à conta do iCloud e às informações do usuário armazenadas no , tudo isso enquanto ainda protege a privacidade e a segurança do usuário.

Os contêineres são totalmente gerenciados pelo desenvolvedor do aplicativo por meio do portal WWDR. O namespace do contêiner é global em todos os desenvolvedores da Apple, portanto, o contêiner não deve ser apenas exclusivo para aplicativos de um determinado desenvolvedor, mas para todos os desenvolvedores e aplicativos da Apple.

A Apple sugere o uso da notação DNS reversa ao criar o namespace para contêineres de aplicativos. Exemplo: iCloud.com.company-name.application-name

Embora os contêineres estejam, por padrão, associados um a um determinado aplicativo, eles podem ser compartilhados entre aplicativos. Portanto, vários aplicativos podem coordenar em um único contêiner. Um único aplicativo também pode conversar com vários contêineres.

Bancos de dados

Uma das principais funções do CloudKit é usar o modelo de dados e a replicação de um aplicativo que modelam até os servidores iCloud. Algumas informações se destinam ao usuário que a criou, outras informações são dados públicos que podem ser criados por um usuário para uso público (como uma revisão de restaurante) ou podem ser informações que o desenvolvedor publicou para o aplicativo. Em ambos os casos, o público não é apenas um único usuário, mas é uma comunidade de pessoas.

Diagrama de contêiner do CloudKit

Dentro de um Contêiner, em primeiro lugar está o banco de dados público. É aqui que todas as informações públicas vivem e se misturam. Além disso, há vários bancos de dados privados individuais para cada usuário do aplicativo.

Ao ser executado em um dispositivo iOS, o aplicativo só terá acesso às informações do usuário do iCloud conectado no momento. Portanto, a exibição do aplicativo do contêiner será a seguinte:

A exibição de aplicativos do contêiner

Ele só pode ver o banco de dados público e o banco de dados privado associado ao usuário do iCloud conectado no momento.

Os bancos de dados são expostos no CloudKit Framework por meio da CKDatabase classe . Cada aplicativo tem acesso a dois bancos de dados: o banco de dados público e o privado.

O Contêiner é o ponto de entrada inicial no CloudKit. O código a seguir pode ser usado para acessar o banco de dados público e privado do contêiner padrão do aplicativo:

using CloudKit;
//...

public CKDatabase PublicDatabase { get; set; }
public CKDatabase PrivateDatabase { get; set; }
//...

// Get the default public and private databases for
// the application
PublicDatabase = CKContainer.DefaultContainer.PublicCloudDatabase;
PrivateDatabase = CKContainer.DefaultContainer.PrivateCloudDatabase;

Aqui estão as diferenças entre os tipos de banco de dados:

Banco de dados público Banco de Dados Privado
Tipo de Dados Dados Compartilhados Dados do usuário atual
Cota Contabilizado na Cota do Desenvolvedor Contabilizado na Cota do Usuário
Permissões padrão Mundo legível Legível pelo usuário
Editando permissões Funções do painel do iCloud por meio de um nível de classe de registro N/D

Registros

Os contêineres mantêm bancos de dados e, dentro dos bancos de dados, há registros. Os registros são o mecanismo no qual os dados estruturados são movidos de e para o CloudKit:

Os contêineres mantêm bancos de dados e, dentro de bancos de dados, há registros

Os registros são expostos no CloudKit Framework por meio da CKRecord classe , que encapsula pares chave-valor. Uma instância de um objeto em um aplicativo é equivalente a uma CKRecord no CloudKit. Além disso, cada CKRecord um possui um tipo de registro, que é equivalente à classe de um objeto .

Os registros têm um esquema just-in-time, portanto, os dados são descritos como CloudKit antes de serem entregues para processamento. A partir desse ponto, o CloudKit interpretará as informações e lidará com a logística de armazenar e recuperar o registro.

A CKRecord classe também dá suporte a uma ampla gama de metadados. Por exemplo, um registro contém informações sobre quando ele foi criado e o usuário que o criou. Um registro também contém informações sobre quando foi modificado pela última vez e o usuário que o modificou.

Os registros contêm a noção de uma marca de alteração. Esta é uma versão anterior de uma revisão de um determinado registro. A Marca de Alteração é usada como uma maneira leve de determinar se o cliente e o servidor têm a mesma versão de um determinado registro.

Conforme indicado acima, CKRecords encapsule pares chave-valor e, como tal, os seguintes tipos de dados podem ser armazenados em um registro:

  1. NSString
  2. NSNumber
  3. NSData
  4. NSDate
  5. CLLocation
  6. CKReferences
  7. CKAssets

Além dos tipos de valor único, um registro pode conter uma matriz homogênea de qualquer um dos tipos listados acima.

O código a seguir pode ser usado para criar um novo registro e armazená-lo em um banco de dados:

using CloudKit;
//...

private const string ReferenceItemRecordName = "ReferenceItems";
//...

var newRecord = new CKRecord (ReferenceItemRecordName);
newRecord ["name"] = (NSString)nameTextField.Text;
await CloudManager.SaveAsync (newRecord);

Zonas de registro

Os registros não existem sozinhos em um determinado banco de dados – os grupos de registros existem juntos dentro de uma Zona de Registro. As Zonas de Registro podem ser consideradas como Tabelas em bancos de dados relacionais tradicionais:

Grupos de registros existem juntos dentro de uma Zona de Registro

Pode haver vários registros dentro de uma determinada Zona de Registro e várias Zonas de Registro em um determinado banco de dados. Cada banco de dados contém uma zona de registro padrão:

Cada banco de dados contém uma zona de registro padrão e uma zona personalizada

É aqui que os registros são armazenados por padrão. Além disso, zonas de registro personalizadas podem ser criadas. As Zonas de Registro representam a granularidade base na qual as Confirmações Atômicas e Controle de Alterações são feitas.

Identificadores de registro

Os Identificadores de Registro são representados como uma tupla, contendo um nome de registro fornecido pelo cliente e a zona na qual o registro existe. Os identificadores de registro têm as seguintes características:

  • Eles são criados pelo aplicativo cliente.
  • Eles são totalmente normalizados e representam o local específico do registro.
  • Ao atribuir a ID exclusiva de um registro em um banco de dados estrangeiro ao nome do registro, eles podem ser usados para fazer a ponte entre bancos de dados locais que não são armazenados no CloudKit.

Quando os desenvolvedores criam novos registros, eles podem optar por passar um Identificador de Registro. Se um Identificador de Registro não for especificado, um UUID será criado e atribuído automaticamente ao registro.

Quando os desenvolvedores criam novos Identificadores de Registro, eles podem optar por especificar a Zona de Registro à qual cada registro pertencerá. Se nenhum for especificado, a Zona de Registro Padrão será usada.

Os identificadores de registro são expostos no CloudKit Framework por meio da CKRecordID classe . O código a seguir pode ser usado para criar um novo Identificador de Registro:

var recordID =  new CKRecordID("My Record");

Referências

As referências fornecem relações entre registros relacionados em um determinado banco de dados:

As referências fornecem relações entre registros relacionados em um determinado banco de dados

No exemplo acima, os Pais possuem Filhos para que a Criança seja um registro filho do registro pai. A Relação vai do registro filho para o registro pai e é conhecida como referência de back.

As referências são expostas no CloudKit Framework por meio da CKReference classe . Eles são uma maneira de permitir que o servidor iCloud entenda a relação entre registros.

As referências fornecem o mecanismo por trás de Exclusões em Cascata. Se um registro pai for excluído do banco de dados, todos os registros filho (conforme especificado em uma Relação) também serão excluídos automaticamente do banco de dados.

Observação

Ponteiros pendentes são uma possibilidade ao usar o CloudKit. Por exemplo, quando o aplicativo busca uma lista de ponteiros de registro, seleciona um registro e solicita o registro, o registro pode não existir mais no banco de dados. Um aplicativo deve ser codificado para lidar com essa situação normalmente.

Embora não seja necessário, referências de back são preferenciais ao trabalhar com o CloudKit Framework. A Apple ajustou o sistema para tornar esse o tipo de referência mais eficiente.

Ao criar uma Referência, o desenvolvedor pode fornecer um registro que já esteja na memória ou criar uma referência a um Identificador de Registro. Se o uso de um Identificador de Registro e a referência especificada não existir no banco de dados, um Ponteiro Pendente será criado.

Veja a seguir um exemplo de criação de uma referência em relação a um Registro conhecido:

var reference = new CKReference(newRecord, new CKReferenceAction());

Ativos

Os ativos permitem que um arquivo de dados grandes e não estruturados seja carregado no iCloud e associado a um determinado Registro:

Os ativos permitem que um arquivo de dados grandes e não estruturados seja carregado no iCloud e associado a um determinado Registro

No cliente, um CKRecord é criado que descreve o arquivo que será carregado no servidor iCloud. Um CKAsset é criado para conter o arquivo e está vinculado ao registro que o descreve.

Quando o arquivo é carregado no servidor, o registro é colocado no banco de dados e o arquivo é copiado em um banco de dados especial de Armazenamento em Massa. Um link é criado entre o ponteiro de registro e o arquivo carregado.

Os ativos são expostos no CloudKit Framework por meio da CKAsset classe e são usados para armazenar dados grandes e não estruturados. Como o desenvolvedor nunca deseja ter dados grandes e não estruturados na memória, os ativos são implementados usando arquivos em disco.

Os ativos pertencem a registros, o que permite que os ativos sejam recuperados do iCloud usando o registro como ponteiro. Dessa forma, o servidor pode Coletar Ativos de Coleta de Lixo quando o registro que possui o Ativo for excluído.

Como CKAssets se destinam a lidar com arquivos de dados grandes, a Apple projetou o CloudKit para carregar e baixar com eficiência os Ativos.

O código a seguir pode ser usado para criar um Ativo e associá-lo ao Registro:

var fileUrl = new NSUrl("LargeFile.mov");
var asset = new CKAsset(fileUrl);
newRecord ["name"] = asset;

Agora abordamos todos os objetos fundamentais no CloudKit. Os contêineres são associados a aplicativos e contêm bancos de dados. Os bancos de dados contêm registros agrupados em Zonas de Registro e apontados por Identificadores de Registro. As relações pai-filho são definidas entre Registros usando Referências. Por fim, arquivos grandes podem ser carregados e associados a Registros usando Ativos.

API de Conveniência do CloudKit

A Apple oferece dois conjuntos de API diferentes para trabalhar com o CloudKit:

  • API operacional – oferece todos os recursos do CloudKit. Para aplicativos mais complexos, essa API fornece controle refinado sobre o CloudKit.
  • API de conveniência – oferece um subconjunto comum pré-configurado de recursos do CloudKit. Ele fornece uma solução de acesso fácil e conveniente para incluir a funcionalidade do CloudKit em um aplicativo iOS.

A API de Conveniência geralmente é a melhor opção para a maioria dos aplicativos iOS e a Apple sugere começar com ela. O restante desta seção abordará os seguintes tópicos da API de Conveniência:

  • Salvando um registro.
  • Buscando um registro.
  • Atualizando um registro.

Código de instalação comum

Antes de começar a usar a API de Conveniência do CloudKit, há um código de instalação padrão necessário. Comece modificando o arquivo do AppDelegate.cs aplicativo e faça com que ele se pareça com o seguinte:

using System;
using System.Collections.Generic;
using System.Linq;
using Foundation;
using UIKit;
using CloudKit;

namespace CloudKitAtlas
{
    [Register ("AppDelegate")]
    public partial class AppDelegate : UIApplicationDelegate
    {
        public override UIWindow Window { get; set;}
        public CKDatabase PublicDatabase { get; set; }
        public CKDatabase PrivateDatabase { get; set; }

        public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
        {
            application.RegisterForRemoteNotifications ();

            // Get the default public and private databases for
            // the application
            PublicDatabase = CKContainer.DefaultContainer.PublicCloudDatabase;
            PrivateDatabase = CKContainer.DefaultContainer.PrivateCloudDatabase;

            return true;
        }

        public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken)
        {
            Console.WriteLine ("Registered for Push notifications with token: {0}", deviceToken);
        }

        public override void FailedToRegisterForRemoteNotifications (UIApplication application, NSError error)
        {
            Console.WriteLine ("Push subscription failed");
        }

        public override void ReceivedRemoteNotification (UIApplication application, NSDictionary userInfo)
        {
            Console.WriteLine ("Push received");
        }
    }
}

O código acima expõe os bancos de dados cloudkit públicos e privados como atalhos para facilitar o trabalho no restante do aplicativo.

Em seguida, adicione o seguinte código a qualquer exibição ou contêiner de exibição que usará o CloudKit:

using CloudKit;
//...

public AppDelegate ThisApp {
    get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
}

Isso adiciona um atalho para acessar o AppDelegate e acessar os atalhos de banco de dados públicos e privados criados acima.

Com esse código em vigor, vamos dar uma olhada na implementação da API de Conveniência do CloudKit em um aplicativo Xamarin iOS 8.

Salvando um registro

Usando o padrão apresentado acima ao discutir Registros, o código a seguir criará um novo registro e usará a API de Conveniência para salvá-lo no banco de dados público:

private const string ReferenceItemRecordName = "ReferenceItems";
...

// Create a new record
var newRecord = new CKRecord (ReferenceItemRecordName);
newRecord ["name"] = (NSString)nameTextField.Text;

// Save it to the database
ThisApp.PublicDatabase.SaveRecord(newRecord, (record, err) => {
    // Was there an error?
    if (err != null) {
        ...
    }
});

Três coisas a serem observadas sobre o código acima:

  1. Ao chamar o SaveRecord método do PublicDatabase, o desenvolvedor não precisa especificar como os dados são enviados, em qual Zona ele está sendo gravado etc. A API de Conveniência está cuidando de todos esses detalhes em si.
  2. A chamada é assíncrona e fornece uma rotina de retorno de chamada quando a chamada é concluída, seja com êxito ou falha. Se a chamada falhar, uma mensagem de erro será fornecida.
  3. O CloudKit não fornece armazenamento/persistência local; é apenas uma mídia de transferência. Portanto, quando uma solicitação é feita para salvar um Registro, ele é enviado imediatamente para os servidores iCloud.

Observação

Devido à natureza "perdida" das comunicações de rede móvel, em que as conexões estão constantemente sendo descartadas ou interrompidas, uma das primeiras considerações que o desenvolvedor deve fazer ao trabalhar com o CloudKit é o tratamento de erros.

Buscando um registro

Com um Registro criado e armazenado com êxito no servidor iCloud, use o seguinte código para recuperar o registro:

// Create a record ID and fetch the record from the database
var recordID = new CKRecordID("MyRecordName");
ThisApp.PublicDatabase.FetchRecord(recordID, (record, err) => {
    // Was there an error?
    if (err != null) {
        ...
    }
});

Assim como ao salvar o Record, o código acima é assíncrono, simples e requer um ótimo tratamento de erros.

Atualizando um registro

Depois que um Registro for buscado dos servidores iCloud, o código a seguir poderá ser usado para modificar o Registro e salvar as alterações de volta no banco de dados:

// Create a record ID and fetch the record from the database
var recordID = new CKRecordID("MyRecordName");
ThisApp.PublicDatabase.FetchRecord(recordID, (record, err) => {
    // Was there an error?
    if (err != null) {

    } else {
        // Modify the record
        record["name"] = (NSString)"New Name";

        // Save changes to database
        ThisApp.PublicDatabase.SaveRecord(record, (r, e) => {
            // Was there an error?
            if (e != null) {
                 ...
            }
        });
    }
});

O FetchRecord método do PublicDatabase retornará um CKRecord se a chamada tiver sido bem-sucedida. Em seguida, o aplicativo modifica o Registro e chama SaveRecord novamente para gravar as alterações no banco de dados.

Esta seção mostrou o ciclo típico que um aplicativo usará ao trabalhar com a API de Conveniência do CloudKit. O aplicativo salvará Registros no iCloud, recuperará esses registros do iCloud, modificará os Registros e salvará essas alterações no iCloud.

Projetando para escalabilidade

Até agora, este artigo analisou como armazenar e recuperar todo o modelo de objeto de um aplicativo dos servidores iCloud, toda vez que ele será trabalhado. Embora essa abordagem funcione bem com uma pequena quantidade de dados e uma base de usuários muito pequena, ela não é bem dimensionada quando a quantidade de informações e/ou base de usuários aumenta.

Big Data, dispositivo minúsculo

Quanto mais popular um aplicativo se torna, mais dados no banco de dados e menos viável é ter um cache de todos esses dados no dispositivo. As seguintes técnicas podem ser usadas para resolver esse problema:

  • Mantenha os dados grandes na nuvem – o CloudKit foi projetado para lidar com dados grandes com eficiência.
  • O cliente só deve exibir uma fatia desses dados – reduzir o mínimo de dados necessários para lidar com qualquer tarefa em um determinado momento.
  • As exibições do cliente podem ser alteradas – como cada usuário tem preferências diferentes, a fatia de dados que está sendo exibida pode mudar de usuário para usuário e a exibição individual do usuário de qualquer fatia pode ser diferente.
  • O cliente usa consultas para concentrar o ponto de vista – as consultas permitem que o usuário exiba um pequeno subconjunto de um conjunto de dados maior que existe na nuvem.

Consultas

Conforme mencionado acima, as consultas permitem que o desenvolvedor selecione um pequeno subconjunto do conjunto de dados maior que existe na nuvem. As consultas são expostas no CloudKit Framework por meio da CKQuery classe .

Uma consulta combina três coisas diferentes: um Tipo de Registro ( RecordType), um Predicado ( NSPredicate) e, opcionalmente, um Descritor de Classificação ( NSSortDescriptors). O CloudKit dá suporte à maior parte do NSPredicate.

Predicados com suporte

O CloudKit dá suporte aos seguintes tipos de NSPredicates ao trabalhar com consultas:

  1. Registros correspondentes em que o nome é igual a um valor armazenado em uma variável:

    NSPredicate.FromFormat(string.Format("name = '{0}'", recordName))
    
  2. Permite que a correspondência seja baseada em um valor de chave dinâmica, para que a chave não precise ser sabeda em tempo de compilação:

    NSPredicate.FromFormat(string.Format("{0} = '{1}'", key, value))
    
  3. Correspondendo a registros em que o valor do Registro é maior que o valor fornecido:

    NSPredicate.FromFormat(string.Format("start > {0}", (NSDate)date))
    
  4. Registros correspondentes em que a localização do Record está a 100 metros do local determinado:

    var location = new CLLocation(37.783,-122.404);
    var predicate = NSPredicate.FromFormat(string.Format("distanceToLocation:fromLocation(Location,{0}) < 100", location));
    
  5. O CloudKit dá suporte a uma pesquisa com tokens. Essa chamada criará dois tokens, um para after e outro para session. Ele retornará um Record que contém esses dois tokens:

    NSPredicate.FromFormat(string.Format("ALL tokenize({0}, 'Cdl') IN allTokens", "after session"))
    
  6. O CloudKit dá suporte a predicados compostos ingressados usando o AND operador .

    NSPredicate.FromFormat(string.Format("start > {0} AND name = '{1}'", (NSDate)date, recordName))
    

Criando consultas

O código a seguir pode ser usado para criar um CKQuery em um aplicativo Xamarin iOS 8:

var recordName = "MyRec";
var predicate = NSPredicate.FromFormat(string.Format("name = '{0}'", recordName));
var query = new CKQuery("CloudRecords", predicate);

Primeiro, ele cria um Predicado para selecionar apenas os registros que correspondem a um determinado nome. Em seguida, ele cria uma consulta que selecionará Registros do Tipo de Registro fornecido que correspondem ao Predicado.

Executando uma consulta

Depois que uma consulta tiver sido criada, use o seguinte código para executar a consulta e processar os registros retornados:

var recordName = "MyRec";
var predicate = NSPredicate.FromFormat(string.Format("name = {0}", recordName));
var query = new CKQuery("CloudRecords", predicate);

ThisApp.PublicDatabase.PerformQuery(query, CKRecordZone.DefaultRecordZone().ZoneId, (NSArray results, NSError err) => {
    // Was there an error?
    if (err != null) {
       ...
    } else {
        // Process the returned records
        for(nint i = 0; i < results.Count; ++i) {
            var record = (CKRecord)results[i];
        }
    }
});

O código acima usa a consulta criada acima e a executa no Banco de Dados Público. Como nenhuma Zona de Registro é especificada, todas as zonas são pesquisadas. Se nenhum erro tiver ocorrido, uma matriz de CKRecords será retornada correspondendo aos parâmetros da consulta.

A maneira de pensar em Consultas é que elas são pesquisas e são ótimas em dividir grandes conjuntos de dados. As consultas, no entanto, não são adequadas para conjuntos de dados grandes, principalmente estáticos, devido aos seguintes motivos:

  • Eles são ruins para a duração da bateria do dispositivo.
  • Eles são ruins para o tráfego de rede.
  • Eles são ruins para a experiência do usuário porque as informações que eles veem são limitadas pela frequência com que o aplicativo está sondando o banco de dados. Os usuários esperam hoje notificações por push quando algo muda.

Assinaturas

Ao lidar com conjuntos de dados grandes e principalmente estáticos, a consulta não deve ser executada no dispositivo cliente, ela deve ser executada no servidor em nome do cliente. A consulta deve ser executada em segundo plano e deve ser executada depois que cada registro for salvo, seja pelo dispositivo atual ou por outro dispositivo tocando no mesmo banco de dados.

Por fim, uma notificação por push deve ser enviada para todos os dispositivos anexados ao banco de dados quando a consulta do lado do servidor é executada.

As assinaturas são expostas no CloudKit Framework por meio da CKSubscription classe . Eles combinam um Tipo de Registro ( RecordType), um Predicado ( NSPredicate) e uma Notificação por Push da Apple ( Push).

Observação

Os pushes do CloudKit são ligeiramente aumentados, pois contêm uma carga contendo informações específicas do CloudKit, como o que causou o push.

Como as assinaturas funcionam

Antes de implementar a Assinatura no código C#, vamos ter uma visão geral rápida de como as assinaturas funcionam:

Uma visão geral de como as assinaturas funcionam

O grafo acima mostra o processo de assinatura típico da seguinte maneira:

  1. O dispositivo cliente cria uma nova Assinatura que contém o conjunto de condições que disparará a assinatura e uma Notificação por Push que será enviada quando o gatilho ocorrer.
  2. A Assinatura é enviada ao Banco de Dados, onde é adicionada à coleção de assinaturas existentes.
  3. Um segundo dispositivo cria um novo Registro e salva esse registro no Banco de Dados.
  4. O Banco de Dados pesquisa em sua lista de Assinaturas para ver se o novo Registro corresponde a qualquer uma de suas condições.
  5. Se uma correspondência for encontrada, a Notificação por Push será enviada para o dispositivo que registrou a Assinatura com informações sobre o Registro que o fez ser disparado.

Com esse conhecimento em vigor, vamos examinar a criação de assinaturas em um aplicativo Xamarin iOS 8.

Criando assinaturas

O código a seguir pode ser usado para criar uma Assinatura:

// Create a new subscription
DateTime date;
var predicate = NSPredicate.FromFormat(string.Format("start > {0}", (NSDate)date));
var subscription = new CKSubscription("RecordType", predicate, CKSubscriptionOptions.FiresOnRecordCreation);

// Describe the type of notification
var notificationInfo = new CKNotificationInfo();
notificationInfo.AlertLocalizationKey = "LOCAL_NOTIFICATION_KEY";
notificationInfo.SoundName = "ping.aiff";
notificationInfo.ShouldBadge = true;

// Attach the notification info to the subscription
subscription.NotificationInfo = notificationInfo;

Primeiro, ele cria um Predicado que fornece a condição para disparar a Assinatura. Em seguida, ele cria a assinatura em relação a um Tipo de Registro específico e define a opção de quando o gatilho é testado. Por fim, ele define o tipo de notificação que ocorrerá quando a Assinatura for disparada e a anexará à Assinatura.

Salvando assinaturas

Com a Assinatura criada, o código a seguir o salvará no Banco de Dados:

// Save the subscription to the database
ThisApp.PublicDatabase.SaveSubscription(subscription, (s, err) => {
    // Was there an error?
    if (err != null) {

    }
});

Usando a API de Conveniência, a chamada é assíncrona, simples e fornece fácil tratamento de erros.

Manipulando notificações por push

Se o desenvolvedor já tiver usado aps (notificações por push) da Apple, o processo de lidar com notificações geradas pelo CloudKit deverá ser familiar.

AppDelegate.csNo , substitua a ReceivedRemoteNotification classe da seguinte maneira:

public override void ReceivedRemoteNotification (UIApplication application, NSDictionary userInfo)
{
    // Parse the notification into a CloudKit Notification
    var notification = CKNotification.FromRemoteNotificationDictionary (userInfo);

    // Get the body of the message
    var alertBody = notification.AlertBody;

    // Was this a query?
    if (notification.NotificationType == CKNotificationType.Query) {
        // Yes, convert to a query notification and get the record ID
        var query = notification as CKQueryNotification;
        var recordID = query.RecordId;
    }
}

O código acima solicita que o CloudKit analise o userInfo em uma Notificação do CloudKit. Em seguida, as informações são extraídas sobre o alerta. Por fim, o tipo de notificação é testado e a notificação é tratada adequadamente.

Esta seção mostrou como responder ao problema de Big Data e Dispositivo Minúsculo apresentado acima usando Consultas e Assinaturas. O aplicativo deixará seus dados grandes na nuvem e usará essas tecnologias para fornecer exibições sobre esse conjunto de dados.

Contas de usuário do CloudKit

Conforme observado no início deste artigo, o CloudKit foi criado com base na infraestrutura existente do iCloud. A seção a seguir abordará, em detalhes, como as contas são expostas a um desenvolvedor usando a API do CloudKit.

Autenticação

Ao lidar com contas de usuário, a primeira consideração é a autenticação. O CloudKit dá suporte à autenticação por meio do usuário do iCloud conectado no momento no dispositivo. A autenticação ocorre nos bastidores e é tratada pelo iOS. Dessa forma, os desenvolvedores nunca precisam se preocupar com os detalhes da implementação da autenticação. Eles só testam para ver se um usuário está conectado.

Informações da conta de usuário

O CloudKit fornece as seguintes informações de usuário para o desenvolvedor:

  • Identidade – uma maneira de identificar exclusivamente o usuário.
  • Metadados – a capacidade de salvar e recuperar informações sobre os usuários.
  • Privacidade – todas as informações são tratadas em uma mansão consciente da privacidade. Nada é exposto, a menos que o usuário tenha concordado com ele.
  • Descoberta – oferece aos usuários a capacidade de descobrir seus amigos que estão usando o mesmo aplicativo.

Em seguida, examinaremos esses tópicos em detalhes.

Identidade

Conforme mencionado acima, o CloudKit fornece uma maneira de o aplicativo identificar exclusivamente um determinado usuário:

Identificar exclusivamente um determinado usuário

Há um aplicativo cliente em execução nos dispositivos de um usuário e em todos os Bancos de Dados Privados do Usuário específicos dentro do Contêiner do CloudKit. O aplicativo cliente será vinculado a um desses usuários específicos. Isso se baseia no usuário conectado ao iCloud localmente no dispositivo.

Como isso vem do iCloud, há um rico repositório de backup de Informações do Usuário. E como o iCloud está realmente hospedando o Contêiner, ele pode correlacionar os usuários. No gráfico acima, o usuário cuja conta user@icloud.com do iCloud está vinculada ao cliente atual.

Em uma base contêiner por contêiner, uma ID de usuário exclusiva gerada aleatoriamente é criada e associada à conta do iCloud do usuário (endereço de email). Essa ID de Usuário é retornada ao aplicativo e pode ser usada de qualquer maneira que o desenvolvedor achar adequado.

Observação

Diferentes aplicativos em execução no mesmo dispositivo para o mesmo usuário do iCloud terão IDs de usuário diferentes porque estão conectados a diferentes contêineres do CloudKit.

O código a seguir obtém a ID de usuário do CloudKit para o usuário do iCloud conectado no momento no dispositivo:

public CKRecordID UserID { get; set; }
...

// Get the CloudKit User ID
CKContainer.DefaultContainer.FetchUserRecordId ((recordID, err) => {
    // Was there an error?
    if (err!=null) {
        Console.WriteLine("Error: {0}", err.LocalizedDescription);
    } else {
        // Save user ID
        UserID = recordID;
    }
});

O código acima está solicitando que o Contêiner do CloudKit forneça a ID do usuário conectado no momento. Como essas informações vêm do iCloud Server, a chamada é assíncrona e o tratamento de erros é necessário.

Metadados

Cada usuário no CloudKit tem metadados específicos que os descrevem. Esses metadados são representados como um registro do CloudKit:

Cada usuário no CloudKit tem metadados específicos que os descrevem

Olhando dentro do Banco de Dados Privado para um usuário específico de um Contêiner, há um Record que define esse usuário. Há muitos Registros de Usuário dentro do Banco de Dados Público, um para cada usuário do Contêiner. Uma delas terá uma ID de registro que corresponda à ID de Registro do usuário conectado no momento.

Os Registros de Usuário no Banco de Dados Público são legíveis mundialmente. Eles são tratados, na maioria das vezes, como um Registro comum e têm um tipo de CKRecordTypeUserRecord. Esses registros são reservados pelo sistema e não estão disponíveis para consultas.

Use o seguinte código para acessar um Registro de Usuário:

public CKRecord UserRecord { get; set; }
...

// Get the user's record
PublicDatabase.FetchRecord(UserID, (record ,er) => {
    //was there an error?
    if (er != null) {
        Console.WriteLine("Error: {0}", er.LocalizedDescription);
    } else {
        // Save the user record
        UserRecord = record;
    }
});

O código acima está solicitando que o Banco de Dados Público retorne o Registro de Usuário para a ID do usuário que acessamos acima. Como essas informações vêm do iCloud Server, a chamada é assíncrona e o tratamento de erros é necessário.

Privacidade

O CloudKit era design, por padrão, para proteger a privacidade do usuário conectado no momento. Nenhuma informação de identificação pessoal sobre o usuário é exposta por padrão. Há alguns casos em que o aplicativo exigirá informações limitadas sobre o usuário.

Nesses casos, o aplicativo pode solicitar que o usuário divulgue essas informações. Uma caixa de diálogo será apresentada ao usuário solicitando que ele aceite expor as informações da conta.

Descoberta

Supondo que o usuário tenha aceitado permitir que o aplicativo tenha acesso limitado às informações da conta de usuário, ele poderá ser descoberto para outros usuários do aplicativo:

Um usuário pode ser detectável para outros usuários do aplicativo

O aplicativo cliente está conversando com um Contêiner e o Contêiner está falando sobre o iCloud para acessar as informações do usuário. O usuário pode fornecer um endereço de email e a Descoberta pode ser usada para obter informações sobre o usuário. Opcionalmente, a ID de Usuário também pode ser usada para descobrir informações sobre o usuário.

O CloudKit também fornece uma maneira de descobrir informações sobre qualquer usuário que possa ser amigo do usuário do iCloud conectado no momento consultando todo o Catálogo de Endereços. O Processo do CloudKit efetuará pull do Catálogo de Contatos do usuário e usará os endereços de email para ver se ele pode encontrar outros usuários do aplicativo que correspondam a esses endereços.

Isso permite que o aplicativo aproveite o Catálogo de Contatos do usuário sem fornecer acesso a ele ou pedir ao usuário que aprove o acesso aos contatos. Em nenhum momento as informações de contato são disponibilizadas para o aplicativo, somente o Processo do CloudKit tem acesso.

Para recapitular, há três tipos diferentes de entradas disponíveis para a Descoberta de Usuários:

  • ID de Registro de Usuário – A descoberta pode ser feita na ID de Usuário do usuário atualmente conectado ao CloudKit.
  • Endereço Email do usuário – o usuário pode fornecer um endereço de email e ele pode ser usado para descoberta.
  • Catálogo de Contatos – o catálogo de endereços do usuário pode ser usado para descobrir usuários do aplicativo que têm o mesmo endereço de email listado em seus contatos.

A Descoberta de Usuários retornará as seguintes informações:

  • ID de Registro de Usuário – A ID exclusiva de um usuário no Banco de Dados Público.
  • Nome e Sobrenome – como armazenados no Banco de Dados Público.

Essas informações só serão retornadas para usuários que aceitaram a Descoberta.

O código a seguir descobrirá informações sobre o usuário atualmente conectado ao iCloud no dispositivo:

public CKDiscoveredUserInfo UserInfo { get; set; }
//...

// Get the user's metadata
CKContainer.DefaultContainer.DiscoverUserInfo(UserID, (info, e) => {
    // Was there an error?
    if (e != null) {
        Console.WriteLine("Error: {0}", e.LocalizedDescription);
    } else {
        // Save the user info
        UserInfo = info;
    }
});

Use o seguinte código para consultar todos os usuários no Catálogo de Contatos:

// Ask CloudKit for all of the user's friends information
CKContainer.DefaultContainer.DiscoverAllContactUserInfos((info, er) => {
    // Was there an error
    if (er != null) {
        Console.WriteLine("Error: {0}", er.LocalizedDescription);
    } else {
        // Process all returned records
        for(int i = 0; i < info.Count(); ++i) {
            // Grab a user
            var userInfo = info[i];
        }
    }
});

Nesta seção, abordamos as quatro principais áreas de acesso à conta de um usuário que o CloudKit pode fornecer a um aplicativo. Desde a obtenção da Identidade e dos Metadados do usuário até as políticas de Privacidade incorporadas ao CloudKit e, por fim, a capacidade de Descobrir outros usuários do aplicativo.

Os ambientes de desenvolvimento e produção

O CloudKit fornece ambientes de desenvolvimento e produção separados para os tipos de registro e dados de um aplicativo. O Ambiente de Desenvolvimento é um ambiente mais flexível que está disponível apenas para membros de uma equipe de desenvolvimento. Quando um aplicativo adiciona um novo campo a um registro e salva esse registro no Ambiente de Desenvolvimento, o servidor atualiza as informações do esquema automaticamente.

O desenvolvedor pode usar esse recurso para fazer alterações em um esquema durante o desenvolvimento, o que economiza tempo. Uma ressalva é que, depois que um campo for adicionado a um registro, o tipo de dados associado a esse campo não poderá ser alterado programaticamente. Para alterar o tipo de um campo, o desenvolvedor deve excluir o campo no Painel do CloudKit e adicioná-lo novamente com o novo tipo.

Antes de implantar o aplicativo, o desenvolvedor pode migrar seu esquema e dados para o ambiente de produção usando o Painel do CloudKit. Ao executar no Ambiente de Produção, o servidor impede que um aplicativo altere o esquema programaticamente. O desenvolvedor ainda pode fazer alterações com o Painel do CloudKit , mas as tentativas de adicionar campos a um registro no Ambiente de Produção resultam em erros.

Observação

O Simulador do iOS funciona apenas com o Ambiente de Desenvolvimento. Quando o desenvolvedor estiver pronto para testar um aplicativo em um Ambiente de Produção, um dispositivo iOS físico será necessário.

Enviar um aplicativo habilitado para CloudKit

Antes de enviar um aplicativo que usa o CloudKit, ele precisará ser configurado para direcionar o Ambiente do CloudKit de Produção ou o aplicativo será rejeitado pela Apple.

Faça o seguinte:

  1. No Visual Studio para Ma, compile o aplicativo para a versão do>dispositivo iOS:

    Compilar o aplicativo para Release

  2. No menu Compilar , selecione Arquivo Morto:

    Selecionar Arquivo Morto

  3. O Arquivo Morto será criado e exibido no Visual Studio para Mac:

    O Arquivo Morto será criado e exibido

  4. Inicie o Xcode.

  5. No menu Janela , selecione Organizador:

    Selecionar Organizador

  6. Selecione o arquivo morto do aplicativo e clique no botão Exportar... :

    O arquivo morto do aplicativo

  7. Selecione um método para exportação e clique no botão Avançar :

    Selecionar um método para exportação

  8. Selecione a Equipe de Desenvolvimento na lista suspensa e clique no botão Escolher :

    Selecione a Equipe de Desenvolvimento na lista suspensa

  9. Selecione Produção na lista suspensa e clique no botão Avançar :

    Selecione Produção na lista suspensa

  10. Examine a configuração e clique no botão Exportar :

    Examinar a configuração

  11. Escolha um local para gerar o arquivo de aplicativo .ipa resultante.

O processo é semelhante para enviar o aplicativo diretamente para o iTunes Connect, basta clicar no botão Enviar... em vez de exportar... depois de selecionar um Arquivo Morto na janela Organizador.

Quando usar o CloudKit

Como vimos neste artigo, o CloudKit fornece uma maneira fácil para um aplicativo armazenar e recuperar informações dos servidores iCloud. Dito isto, o CloudKit não obsoleto nem substitui nenhuma das ferramentas ou estruturas existentes.

Casos de uso

Os seguintes casos de uso devem ajudar o desenvolvedor a decidir quando usar uma estrutura ou tecnologia específica do iCloud:

  • iCloud Key-Value Store – mantém de forma assíncrona uma pequena quantidade de dados atualizada e é ótimo para trabalhar com as preferências do aplicativo. No entanto, ele é restrito para uma quantidade muito pequena de informações.
  • iCloud Drive – criado com base nas APIs de Documentos do iCloud existentes e fornece uma API simples para sincronizar dados não estruturados do sistema de arquivos. Ele fornece um cache offline completo no Mac OS X e é ótimo para aplicativos centrados em documentos.
  • ICloud Core Data – permite que os dados sejam replicados entre todos os dispositivos do usuário. Os dados são de usuário único e ótimos para manter dados privados e estruturados em sincronia.
  • CloudKit – fornece dados públicos em massa e estrutura e é capaz de lidar com grandes conjuntos de dados e arquivos grandes não estruturados. Ele está vinculado à conta do iCloud do usuário e fornece transferência de dados direcionada pelo cliente.

Tendo esses casos de uso em mente, o desenvolvedor deve escolher a tecnologia correta do iCloud para fornecer a funcionalidade de aplicativo necessária atual e fornecer uma boa escalabilidade para o crescimento futuro.

Resumo

Este artigo abordou uma rápida introdução à API do CloudKit. Ele mostrou como provisionar e configurar um aplicativo Xamarin iOS para usar o CloudKit. Ele abordou os recursos da API de Conveniência do CloudKit. Ele mostrou como criar um aplicativo habilitado para CloudKit para escalabilidade usando Consultas e Assinaturas. Por fim, ele mostrou as informações da Conta de Usuário expostas a um aplicativo pelo CloudKit.