Core Data Lightweight Migration — Como usar?

Bruno Cardoso Ambrosio
Academy@EldoradoCPS
5 min readAug 21, 2020
Assista o vídeo se preferir algo mais audiovisual

O que é “data migration”?

Migration é o que precisamos fazer quando, por qualquer motivo, nosso database (DB) precisa de alterações — sejam alterações nas entidades, nos atributos ou nos relacionamentos.

No Core Data, para alguns tipos de alterações, temos uma ferramenta muito útil que consegue lidar automaticamente com algumas ações de migração. É sobre essa ferramenta que iremos falar hoje — Lightweight migration.

Antes de irmos para os exemplos, vamos à lista de situações em que o lightweight migration pode resolver nossos problemas:

  • Deletar entidades, atributos ou relacionamentos;
  • Renomear entidades, atributos ou relacionamentos usando o renamingIdentifier;
  • Adicionar um novo atributo como optional;
  • Adicionar um novo atributo como required e com um valor padrão;
  • Alterar um atributo optional para required especificando também um valor padrão;
  • Alterar um atributo required para optional;
  • Alterar a hierarquia de entidades;
  • Adicionar uma nova “entidade pai” e mover atributos para cima e pra baixo na hierarquia;
  • Alterar um relacionamento de to-one para to-many;
  • Alterar um relacionamento de non-ordered to-many para ordered to-many(e vice versa)

Vamos à pratica!! (uhull)

Se quiser acompanhar com a mão na massa, baixe a primeira versão do código.

Imagine o seguinte… temos um app que gerencia uma lista de tarefas, guardamos isso num DB com a seguinte entidade:

Código gerado pelo core data comprimido em um único arquivo

Situação:

O booleano “completed” não atende às convenções de nomeação de booleanos — Que diz que é necessário ter o prefixo “is” ou “has” nesse tipo de variável, sendo assim, isso merece nossa atenção.
Além disso, nossos usuários gostariam de poder ranquear suas tarefas em níveis de prioridade, então acrescentaremos essa feature ao nosso app também (e ao DB).

Então nos temos duas tarefas à fazer.

Antes de mais nada

A primeira coisa que precisamos fazer se queremos usar o migration é adicionar uma nova versão de modelo ao core data, então vamos ao passo a passo:

  • Abra o .xcdatamodeld na raiz do app
  • No menu editor, clique em Add Model Version…
  • Você pode nomear como quiser, mas aconselho que apenas acrescente “v2_” no inicio, ficando assim”v2_TodoApp” (e faça isso pra todos, então nas próximas “v3_”, “v4_”), na frente do “v” você pode também usar a própria versão do app.
Resultado do passo anterior
  • Agora você possui duas versões de modelo, ainda falta um passo para começarmos a alterar.
  • Clique no modelo novo e em seguida selecione-o como current model version
Selecionando a versão como a atual do app
  • O checkmark verde mudando de modelo significa que tudo esta pronto para começarmos as alterações (asmudanças nos modelos são registradas na versão selecionada como current)
v2_TodoApp.xcdatamodel selecionado

Mudar os nomes dos atributos

Primeiro exemplo é bem simples, é necessário um único passo: com o v2_ aberto, vá no atributo “completed” e mude o name para “isCompleted”, depois disso, insira o renaming ID dele “completed” — fazendo isso estamos sinalizando para o coreData que o antigo “completed” passa a ser “isCompleted” nessa versão.

Alterando nome de atributo e definindo o renaming ID

Você pode deletar as classes e gerar novamente pelo Editor > Create NSManagedObject Subclass (lembre-se de selecionar a pasta model), ou ir direto no arquivo Todo+CoreDataClass.swift e alterar a variável lá. Também será necessário alterar no resto do app onde aquela variável era usada.

Adicionar um atributo para os níveis de prioridade

Crie um novo arquivo swift chamado TodoPriority.swift e cole o código a seguir dentro dele:

O novo atributo que iremos criar, priorityLevel, será controlado pelo enum em nosso app e isso nos traz um ponto legal para discutirmos, o core data só sabe lidar com certos tipos de dados e o nosso enum não faz parte deles, mas sem pânico, temos pelo menos 2 abordagens para resolver esse detalhe:

ValueTransformers

Um Value transformer é uma classe que “traduz” o seu dado em um outro tipo, um que o Core Data conheça, criar um custom value transformer seria a solução mais difícil para os iniciantes, mas além disso, essa solução não é tão usada (pelo menos nas minhas experiencias), por trazer mais trabalho que o necessário visto que há outra solução mais prática… Vamos à ela.

PropertyAccessors

Os Property Accessors são gerados automaticamente para nossos atributos @NSManaged em tempo de execução, mas podemos assumir o controle e gerar nossos próprios accessors.
Com isso nos podemos fazer duas versões do atributo, uma primitiva usada pelo core data e outra do tipo do enum para ser usada pelo restante do app, iremos seguir nessa abordagem, vamos lá.

  • Abra nosso modelo v2_ e adicione um novo atributo chamado “priorityLevelPrimitive” do tipo Integer 16(mesmo tipo do rawValue do nosso enum — poderia ser qualquer um, mas escolhemos esse por ocupar menos espaço), desmarque o optional e pode deixar o valor padrão dele como 0.
  • Bom, vamos agora adicionar nossa variável do tipo do enum para podermos usar no resto do sistema. Abra o arquivo Todo+CoreDataClass.swift vamos fazer neste arquivo porque o core data não o gera de novo a menos que nos deletemos ele. E cole o código abaixo dentro da classe Todo:
Código para ser adicionado em Todo+CoreDataClass.swift

Explicando:
//1 — Criamos a variável primitiva do modelo no código e tiramos a visibilidade do sistema sobre ela
//2 — Criamos uma nova variável usando o enum e usamos o get/set para trabalhar em conjunto com a variável primitiva

Sempre precisamos acionar o will/did Access/Change quando estamos lendo/escrevendo em um accessor

Legal né?! Agora seriam necessárias varias mudanças no código da UI e na própria UI para ter o seletor de prioridades, e também mudar o nome "completed" para "isCompleted" na onde estávamos usando… Como o foco do artigo é o Lightweight Migration, não vou mostrar como fazer essas alterações, mas sugiro que baixe a versão final onde essas mudanças já estão feitas😉

Agora, graças ao nosso uso do Lightweight Migration, nossos usuários terão seus dados automaticamente migrados ao instalar a atualização e poderão ranquear suas tarefas em ordem de prioridade. Graças a ferramenta que acabamos de aprender a usar, mudanças mais ou menos simples (e até corriqueiras) como essa tendem a ser bem mais tranquilas pra gente e para nossos usuários. Excelente!

--

--

Bruno Cardoso Ambrosio
Academy@EldoradoCPS

Brazilian iOS Developer, Java & Technology lover and psychology enthusiast