Iniciando com Couchbase Lite no iOS

This post is also available in: Inglês

Aqui está uma introdução ao uso do Couchbase Lite para obter persistência de dados no iOS, mostrando os passos mínimos necessários para poder ter esse sistema de armazenamento integrado no seu aplicativo. O texto exige que você tenha um pouco de conhecimento de SQL para fins de comparação, e deve servir como um bom começo se você é mais ou menos novo na área das tecnologias NoSQL. Escrito para acelerar a sua aprendizagem de como todas as peças se encaixam, espero que depois de lê-lo com cuidado, você será mais confiante para trabalhar, mesmo se esteja apenas começando com essa plataforma (pelo menos, eu gostaria de ter tal explicação quando comecei). Se você está procurando informações mais detalhadas sobre um tema específico (por exemplo referência API), recomendo pesquisar dentro da documentação oficial do Couchbase.

O meu objetivo com essa introdução, é o mais simples possível: mostrar como armazenar e recuperar um documento que contém a sua própria informação de contato. À continuação, vou mostrar como armazenar vários documentos, e como recuperá-los de volta.

Apesar de ser simples, isso fornece a base teórica para o desenvolvimento de cenários mais complexos. Comece pequeno, depois cresça.

Antes de começar, vamos ver alguns conceitos importantes do banco de dados do Couchbase NoSQL:

  • Manager:  Primeiro objeto instanciado, que por sua vez gerencia objetos do banco de dados Couchbase Lite. É preciso criar uma instância do “Manager” para poder trabalhar com objetos do Couchbase Lite no seu aplicativo.
  • Database (banco de dados): É o container para documentos, um espaço para consultas, sendo fonte e o alvo das réplicas.
  • “Document” (documento):  Num banco de dados de documentos como o Couchbase Lite, a entidade básica armazenado é chamado de “document” em vez de “row” ou “record”. Um documento pode ser ligado a um anexo.
  • Attachment (anexo): Os anexos armazenam dados associados a um documento, mas não fazem parte do objeto JSON do documento. O objetivo principal dos anexos é fazer com que o armazenamento de grandes dados binários relacionado a um documento seja eficiente.
  • Views:  Eu gosto de pensar numa „View” como se fosse uma tabela desnormalizada do mundo SQL (dinamicamente gerado a partir de objetos JSON). Uma “View” é um índice persistente de documentos num banco de dados, que você consulta para localizar dados. Couchbase Lite não tem uma linguagem de consulta como SQL; em vez disso, ele usa uma técnica chamada de “MapReduce” para gerar índices (Views) de acordo com critérios arbitrários, definidos pelo aplicativo. O componente principal de uma “View” (além do seu nome), é a sua função de mapa. As “Views” são persistentes, e precisam ser atualizadas (progressivamente) sempre que um documento é alterado, portanto, ter um grande número delas pode ser caro. Em vez disso, é melhor ter um número menor de “Views” que podem ser consultados de maneiras interessantes.
  • Query (consulta): É a ação de procurar resultados de um índice (index) de uma “View”. No Couchbase Lite, consultas são objetos gerados pela classe “Query”. Para realizar uma consulta, você cria uma dessas, personaliza as propriedades (tal como o intervalo de chave, ou o número máximo de “rows” (linhas)) e, em seguida,  o executa. O resultado é um “Query Enumerator” (enumerador de consultas), que fornece uma lista de objetos da “QueryRow”, cada um descrevendo uma “row” do índice da “View”.

Tendo esses conceitos em mente, o uso do Couchbase Lite se resume aos seguintes passos:

  1. Adicione as dependências de bibiliotecas ao projeto.
  2. Crie um “Manager”.
  3. Crie um objeto de banco de dados.
  4. Modele seus objetos, salve e recupere-os.
  5. Implementar execução de “Views”, para consultar, salvar e apagar dados.

Vamos direto aos passos:

Passo 1 – Adicione as dependências de bibliotecas ao projeto

Baixe o “framework” CouchbaseLite e inclua-o no projeto, seguindo os procedimentos normais. O código ilustrado aqui é baseado na versão 1.0.2 do Couchbase Lite. Certifique-se que tem os seguintes “frameworks” e bibliotecas vinculados ao seu projeto (linked libraries):

  • CFNetwork.framework
  • Security.framework
  • SystemConfiguration.framework
  • libsqlite3.dylib
  • libz.dylib

À continuação, vamos instanciar o “Manager” + o banco de dados (passo 2 e 3).

3 : Criar “Manager” + objeto do banco de dados 

Esse é normalmente armazenado como “singleton”, já que esse objeto será freqüentemente referenciado (veja exemplos oficiais de códigos Couchbase).

Passo 4 – Modele seus objetos, salve e recupere-os.

Vamos agora à modelação de objetos. Se você tiver um projeto existente onde queira usar Couchbase Lite, os seus objetos provavelmente já estão modelados em objetos nativos (“Model”, como em “Model View Controller”). O último passo será mapear esses objetos numa entidade que pode ser guardada no banco de dados. Essa entidade, você já sabe, é um “Document”.

É possível criar um documento com, ou sem ID atribuído. Se você não precisa ou não deseja definir o seu próprio ID, chame o método do banco de dados (Database) “createDocument”, e o ID será gerado aleatóriamente na forma de um ID universalmente único (UUID – Universally Unique ID), que tem a aparência de uma seqüência de dígitos hexadecimais.

Então temos básicamente duas opções para criá-lo: [database createDocument] ou [database documentWithID]. Escolhendo a segunda opção, um novo documento será gerado, se não houver um outro já existente.

No nosso caso de uso (armanezar própria informação de contato) é melhor usar o nome de usuário para criar um ID único, já que cada usuário vai ter apenas um de tal documento, (que igualmente vai ser único no sistema). Depois de instanciar o objeto de documento, podemos adicionar propriedades a ele, por exemplo assim:

Observe que inseri um campo para “type” (tipo) acima. Eis o porquê: No NoSQL não existe o conceito de “tabelas”, como a que estamos habituados no SQL. No entanto, é um padrão comum usar a propriedade “type” do documento para alcançar o mesmo efeito. Enquanto que no SQL íamos estruturar/ separar os nossos dados com tableas, no Couchbase depois podemos classificar os dados usando a propriedade “type” do documento, quando realizamos uma consulta. É claro que, “type” é apenas uma convenção, você pode nomeá-lo como quiser.

Couchbase Lite suporta as típicas operações “CRUD” de banco de dados nos documentos: “Create, Read, Update, Delete” (criar, ler, atualizar, apagar). No entanto, há uma alternativa para manipular o nosso documento:

No iOS, o Couchbase Lite SDK já provê um mapeador entre objetos nativos e documentos Couchbase Lite. No momento da redação deste texto, isso acontece usando uma classe abstrata chamada “CBLModel”. Segundo a documentação:

CBLModel: Classe de modelo genérica para documentos CouchbaseLite. Há um mapeamento de 1::1 entre estes e documentos “CBL”; use “+modelForDocument”: para obter (ou criar) um objeto modelo para um documento, e “.document” para obter o documento dum modelo. Você deve fazer sua classe uma subclasse do CBLModel, e declarar as propriedades na “@interface” da subclasse normalmente. Tal como acontece com NSManagedObject, você não precisa implementar os seus métodos de acesso ou declarar variáveis de instância; simplesmente marque-os como ‘@dynamic’ na seção “@implementation” da classe (potencialmente substituindo o @synthesize). O valor da propriedade será automaticamente obtida a partir do documento ou armazenado nele, usando o mesmo nome.

Original em ingles (obtido da documentação oficial):

CBLModel: Generic model class for CouchbaseLite documents. There’s a 1::1 mapping between these and CBLDocuments; call +modelForDocument: to get (or create) a model object for a document, and .document to get the document of a model. You should subclass this and declare properties in the subclass’s @interface. As with NSManagedObject, you don’t need to implement their accessor methods or declare instance variables; simply note them as ‘@dynamic’ in the class @implementation. The property value will automatically be fetched from or stored to the document, using the same name.

Isso significa, basicamente, que nem sequer tem que converter objetos para JSON (e vice-versa) para armazenamento. Que conveniente!

Como pode ver, você pode trabalhar com um documento diretamente (documento CBL), ou através um modelo (modelo CBL), ou ambos. Um documento em si, portanto, pode vir como resultado de uma consulta, de um objeto modelo CBL, ou de métodos de banco de dados (por exemplo: [database existingDocumentWithID]).

Então vamos voltar ao trabalho. Já que queremos armanezar as nossas informações de contato, criamos uma classe “ContactInfoModel”, herdando do modelo CBL, para trabalhar com os dados que devem ser armazenados:

Vamos em frente e implementemos métodos para criar o nosso documento usando modelo CBL. Agora a nossa classe de modelo apresenta o seguinte aspecto:

O arquivo “header”:

Agora já podemos armazenar a nossa informação de contato:

A não ser que definamos a propriedade „autosaves” do Modelo CBL como „true” (verdadeiro), os objetos não serão imediatamente salvos no banco de dados quando suas propriedades mudam. Então executamos o mesmo manualmente, com o método [self save: &error], depois que mudamos as propriedades.

Entretanto, lembra que queríamos criar o documento com o nosso próprio ID? Vamos adicionar um método para conseguir isso:

Chamar o método acima duas vezes com o mesmo „thisUserId” (este usuário), retornará o mesmo documento.

Observe que neste exemplo (nós mesmos criando o ID único do documento), a informação do „user_id” se torna duplicada: ela se encontra na propriedade do „user_id”, e além disso, existe como o ID do nosso documento. Porém, por razões de explicação, vou deixar esse campo para que possamos usá-lo durante a explicação de consultas.

Agora estamos criando, salvando e recuperando documentos (pelo ID deles). Mas o que acontece quando temos vários documentos? Ou vários tipos de documentos? Como é possível recuperá-los de volta? Vamos conhecer o sistema „Query” do Couchbase (sistema de consultas) para nos auxiliar com isto.

Passo 5 – Implementar “Views”, consultas (queries), e métodos para salvar e apagar

No Couchbase lite, realizar consultas parece ser um processo mais preparado e dividido quando comprado com SQL.

Aqui está uma visão de alto nível de como os dados são mapeados a partir do banco de dados para o seu objeto nativo:

O banco de dados tem „Views” (vistas). As „Views” podem ser consultadas. As respostas das consultas podem ser pós-processadas (por exemplo: filtradas, ordenadas, somadas). Então não é como uma instrução SQL que faz tudo com „Joins()” e „Counts()”.

Por esta razão, no início, o desenvolvimento pode parecer ir um pouco para trás quando comparado com SQL, onde você simplesmente tem dados e faz consultas à vontade, porque aqui, antes de tudo, é preciso criar „Views”, e só então as consultas. Mas no final o efeito é o mesmo: Você apenas está buscando dados do banco de dados. Mas de uma maneira diferente.

Eu gosto de pensar numa „View” como se fosse uma função (muito parecido com os chamados „Procedures”, „Triggers” e „Modules” em bancos de dados relacionais) que é acionada cada vez que um documento é inserido, como uma preparação desses dados para realizar consultas eficientes.

Sem mais demoras, vamos criar um método para obter dados de contato baseado no nome de usuário (a propriedade “user_id”), em primeiro lugar, criando uma “View”:

Criamos uma “CBLView” chamando o método do banco de dados “viewNamed”. Usando o “setMapBlock” da “CBLView”, todos documentos que tenham o ”type” da propriedade definido como “contactinfo” retornará dois parâmetros: o “user_id” e os dados de contato.

O que este código faz é instalar este procedimento de “view”/”map” para o banco de dados, de modo que ele seja executado para cada documento.

Então não importa o jeito que seja o esquema (estrutura) do seu documento, se ele tem a propriedade “type: contactInfo” na sua raiz, ele será devolvido a partir desta função. Do ponto de vista SQL, eu gosto de pensar nisso como se ele estivesse gerando uma outra “index table” (tabela de índice) com duas colunas (nome de usuário, dados de contato), cada “row” contendo os valores, como no seguinte exemplo:

Screen Shot 2014-09-23 at 2.14.03 PM

(Antes mesmo de tentar, os números de telefone das moças são falsos ☺)

Usando a “CBLView”, finalmente criamos uma consulta usando seu método “createQuery”. Com o objeto “CBLQuery” em escopo, temos a chance de fazer uma filtragem final:

Configuramos a faixa de keys dos resultados da consulta para retornar apenas o que possuem como parâmetro o usuário fornecido.

Então, se chamamos o método acima assim:

O retorno da consulta conterá o subconjunto das informações:

Screen Shot 2014-09-23 at 2.21.04 PM

Não! As moças sumiram! Vamos corrigir isso:

Screen Shot 2014-09-23 at 2.34.27 PM

Isso foi fácil.

Como você pode ver, a variável do nome de usuário coincide com o par chave/valor gerada pela função emit() da tabela de índice.

E se quisermos mapear o resultado de volta para o nosso modelo de objeto, fazer algumas mudanças e salvar? Sem problemas:

 

Conclusão

Então é isso. Se quiser ver o código funcionando por meios de projetos exemplo, veja o “github” do Couchbase:

http://developer.couchbase.com/mobile/develop/samples/samples/index.html

O projeto HelloCBL iOS é um bom ponto de partida para ver o básico em ação – mas ele não mostra como trabalhar com modelos como está descrito aqui. Para isso, verifique os exemplos mais avançados.

Em um artigo futuro, vou compartilhar minhas experiências com o Couchbase Sync Gateway, expandindo no exemplo deste texto – portanto, fique atento!

Cumprimentos!