Skip to content

Instantly share code, notes, and snippets.

@murilogteixeira
Last active January 19, 2020 00:49
Show Gist options
  • Save murilogteixeira/4f4027b9a18c25509aae9112e2b15193 to your computer and use it in GitHub Desktop.
Save murilogteixeira/4f4027b9a18c25509aae9112e2b15193 to your computer and use it in GitHub Desktop.
Tutorial CRUD CoreData

Como criar um CRUD (Create, Retrieve, Update, Delete) usando o CoreData

O que é CoreData?

É uma estrutura de persistência de dados, framework fornecida pela Apple, para ser usada em sistemas operacionais como macOS e iOs e tem como principal funcionalidade a organização dos dados pelo modelo entidade-relacional para ser serializado em XML, binário ou para SQLite, e assim permitindo a manipulação desses utilizando objetos de alto nível representando suas entidades e relacionamentos.

O CoreData permite uma visualização gráfica, como GUI (Graphic User Interface), e o controle da versão serializada provide o ciclo de vida do objeto e o manuseio gráfico do mesmo, mantendo a persistência e sem a necessidade de administrar o banco de dados diretamente, o que poderia ser uma dor de cabeça ao desenvolvedor.

O CoreData fornece ao desenvolvedor uma melhor opção de gerenciamento de dados, sendo estes automatizados e generalizados, CoreData provide a abstração que permite o controle dos modelos de camada em banco de dados de maneira facilitada e utilizando padrões de projetos como Orientação a Objeto e Modelos Relacionais.

Conceitos para melhor entendimento das operações:

NSManagedObject é uma classe que implementa os comportamentos básicos para um objeto que seja receptível para o Core Data, sendo necessário a transformação de determinado objeto para esse tipo.

NSFetchRequest é uma forma de chamada para receber os dados contidos na entidade que foi chamada, por exemplo, NSFetchRequest(entityName "nomeEntidade”), realizando uma consulta para pegar os dados.

NSPersistentContainer é uma implementação que serve como meio de acesso ao banco de dados dentro do Core Data, retornando um container se o banco de dados existe, caso contrário retornando um erro.

NSManagedObjectContext consiste em um grupo de modelos relacionados que tem a função primária de gerenciar a coleção dos objetos do Core Data, que representam os objetos em uma view consistente ou em outros armazenamentos.

Tutorial

Criando o projeto

  1. Crie um novo projeto no XCode
  2. Selecione Single View App e depois Next;
  3. Marque a opção Core Data e depois Next:

Com o projeto criado, observe que um novo arquivo chamado .xcdatamodeld foi criado. Ele contém informações a respeito das entidades, relações, entre outros relacionados aos dados que se deseja persistir.

Com a opção Add Entity você pode criar uma nova entidade. Nesse tutorial vamos chamar de Usuario, então basta renomear a entidade criada com nome de Entity para Usuario. Entendendo o contexto de Banco de Dados, uma nova entidade será criada, onde você poderá criar seus atributos, assim como seus tipos.

Feito isso, vamos criar os nossos atributos nome, idade, login, senha. Basta adicionar no botão + logo abaixo de Attributes, inserir o nome de cada atributo e o seu respectivo tipo.

Após criar suas entidades e seus atributos no arquivo .xcdatamodeld, você irá conseguir fazer CRUD com os dados.

Primeiro importe o CoreData no seu projeto:

import CoreData

Agora devemos recuperar o objeto criado no AppDelegate (persistentContainer) para fazermos as modificações do CoreData e o context que é o objeto que vamos manipular para fazer o CRUD dos nossos dados.

Dentro do nosso ViewController, precisamos recuperar nosso persistentContainer e o context. Para isso, devemos recuperar ele dentro da nossa classe appDelegate. E para acessarmos essa classe, temos que instanciá-la.

O código ficará assim:

class ViewController: UIViewController {

    var appDelegate: AppDelegate!

    var context: NSManagedObjectContext!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        appDelegate = UIApplication.shared.delegate as? AppDelegate

        context = appDelegate.persistentContainer.viewContext
    }
    
}

Após realizar esses dois passos, já temos o objeto que é responsável pelo gerenciamento e manipulação dos nosso dados.

Salvando dados no CoreData (Create)

Para salvar nossos dados, precisamos primeiro fazer a instância da(s) nossa(s) entidades(s). Para facilitar o entendimento, vamos criar funções para cada operação.

Portanto, crie a função salvar logo abaixo do viewDidLoad e instancie a entidade que usaremos para salvar os dados conforme abaixo:

func salvar() {
    let usuarioEntidade = NSEntityDescription.entity(forEntityName: "Usuario", in: context)!

    let usuario = NSManagedObject(entity: usuarioEntidade, insertInto: context)
}

Dentro do parametro forEntityName vamos colocar o nome correto da nossa entidade.

Apos a criação da nossa entidade, podemos persistir dados usando o metodo setValue.

func salvar() {

    //...

    usuario.setValue("Fábio Franca", forKey: "nome")
    usuario.setValue(20, forKey: "idade")
    usuario.setValue("fabio", forKey: "login")
    usuario.setValue("1234", forKey: "senha")
}

O primeiro parâmetro desse método é justamente o dado que queremos salvar, o segundo é o atributo da nossa entidade onde esse dado será salvo.

O último passo é justamente salvar os dados. Para isso, podemos usar o método save do nosso objeto context. (Somos obrigados a colocar esse comando dentro da estrutura do/catch pois pode lançar algum erro durante a sua execução).

func salvar() {

    //...
    
    do {
        try context.save()
        print("dados salvos corretamente!!!"  )
    }
    catch {
        print("erro ao salvar os dados")
    }
}

Após finalizado, a função salvar ficará assim:

func salvar() {
    let usuarioEntidade = NSEntityDescription.entity(forEntityName: "Usuario", in: context)!

    let usuario = NSManagedObject(entity: usuarioEntidade, insertInto: context)

    usuario.setValue("Fábio Franca", forKey: "nome")
    usuario.setValue(20, forKey: "idade")
    usuario.setValue("fabio", forKey: "login")
    usuario.setValue("1234", forKey: "senha")

    do {
        try context.save()
        print("dados salvos corretamente!!!"  )
    }
    catch {
        print("erro ao salvar os dados")
    }
}

Recuperando dados do CoreData (Retrieve)

Para recuperar os dados utilizando o CoreData, precisamos criar uma requisição como primeiro passo.

Assim como salvar, vamos criar uma função para recuperar os dados. Crie a função recuperar conforme abaixo:

func recuperar() {
    let requisicao = NSFetchRequest<NSFetchRequestResult>(entityName: "Usuario")
}

Essa requisição é criada usando a classe NSFetchRequest que retorna o tipo NSFetchRequestResult, pois essa requisição será passada como parâmetro para o método fetch do nosso objeto context. Essa requisição basicamente serve para mostrar quais os dados que queremos recuperar, por isso foi necessário passar como parâmetro o nome da nossa entidade.

Após a criação da nossa requisição, vamos agora recuperar os dados. Para isso, vamos usar o método do nosso objeto context, passando como parâmetro a nossa requisição.

func recuperar() {

    //...

    context.fetch(requisicao)
}

Esse método nos retorna um array de dados da nossa requisição. Para recuperar de fato esses dados, temos que colocá-lo dentro de uma estrutura do/catch e fazer uma referência para esse retorno.

func recuperar() {

    //...

    do {
        let resultado = try context.fetch(requisicao)
        if resultado.count > 0 {
            //...
        }
        else{
            print("nenhum usuario encontrado")
        }
    }
    catch  {
        print("Erro ao recuperar os usuarios")
    }
}

O método fetch, como dito anteriormente, nos retornar um array de objetos do tipo NSObject, mas temos que fazer um casting para o tipo NSManagedObject, pois esse tipo tem alguns métodos que são necessários para a recuperação de dados.

Para listar cada objeto desse array, vamos fazer um foreach e fazer a conversão dentro da própria estrutura de repetição, e posteriormente, devemos recuperar os atributos desses objetos recuperados, para isso, usamos o método value, passando como parâmento o nome dos atributos que queremos.

func recuperar() {

    //...

    do {
        let usuarios = try context.fetch(requisicao)
        if usuarios.count > 0 {
            for usuario in usuarios as! [NSManagedObject]{
                let nomeUsuario = usuario.value(forKey: "nome") as! String
                let idadeUsuario = usuario.value(forKey: "idade") as! Int
                let senhaUsuario = usuario.value(forKey: "senha") as! String
                let loginUsuario = usuario.value(forKey: "login") as! String
                print(nomeUsuario)
                print(idadeUsuario)
                print(senhaUsuario)
                print(loginUsuario)
            }
        }
        else {
            print("nenhum usuario encontrado")
        }
    }
    catch  {
        print("Erro ao recuperar os usuarios")
    }
}

Após isso, a função recuperar ficará assim:

func recuperar() {
    let requisicao = NSFetchRequest<NSFetchRequestResult>(entityName: "Usuario")

    do {
        let resultado = try context.fetch(requisicao)
        if resultado.count > 0 {
            //...
        }
        else{
            print("nenhum usuario encontrado")
        }
    }
    catch  {
        print("Erro ao recuperar os usuarios")
    }

    do {
        let usuarios = try context.fetch(requisicao)
        if usuarios.count > 0 {
            for usuario in usuarios as! [NSManagedObject]{
                let nomeUsuario = usuario.value(forKey: "nome")
                let idadeUsuario = usuario.value(forKey: "idade")
                let senhaUsuario = usuario.value(forKey: "senha")
                let loginUsuario = usuario.value(forKey: "login")
                print(nomeUsuario)
                print(idadeUsuario)
                print(senhaUsuario)
                print(loginUsuario)
            }
        }
        else {
            print("nenhum usuario encontrado")
        }
    }
    catch  {
        print("Erro ao recuperar os usuarios")
    }
}

Aplicando filtros nas recuperação dos dados

O primeiro filtro que vamos fazer é o de ordenação. Para isso, vamos criar uma constante do tipo NSSortDescriptor que recebe 2 parâmetros para realizar a ordenação pelo nome do usuário de forma crescente.

let ordenacaoAZ = NSSortDescriptor(key: "nome", ascending: true)

Para passar esse filtro à nossa requisição, vamos usar o método sortDescriptors, que recebe como parâmetro um array de NSSortDescriptor. Ou seja, podemos fazer mais uma ordenação para uma mesma requisição, a prioridade das ordenações passadas será feita a partir da ordem dos parâmetros.

func recuperar() {
    let requisicao = NSFetchRequest<NSFetchRequestResult>(entityName: "Usuario")

    let ordenacaoAZ = NSSortDescriptor(key: "nome", ascending true)
    let ordenacaoZA = NSSortDescriptor(key: "login", ascending false)

    requisicao.sortDescriptors = [ordenacaoAZ, ordenacaoZA]

    context.fetch(requisicao)

    //...
}

Agora vamos filtrar de outra maneira. Esse método consiste em recuperar dados específicos. Para isso, vamos criar uma constante do tipo NSPredicate que possui vários parâmetros para cada tipo de especificação.

Nesse exemplo vamos usar o filtro que contem o format e o args como parâmetros. O format podemos definir o formato do que você quer buscar, ou seja, será o próprio filtro que você deseja fazer, já o args serão os argumentos desse filtro.

func recuperar() {
    //...

    //Recuperacao apenas do nome EXATO passado como argumento
    let predicteEqual = NSPredicate(format: "nome == %@", "Fábio França")

    //Recuperacao de todos os dados que contem o argumento ([c]-> nao é case sensitive)
    let predicateContains = (format: "nome contains [c] %@", "fábio")

    //Diferente do contains por questao de apenas começar com o argumento
    let predicateBeginswith = (format: "nome beginswith [c] %@", "fábio")

    //Operadores de comparacoes de numeros 
    let predicateCompare = (formart: "idade >= %@", "20")
    
    //...
}

Para passar esses filtros para nossa requisição, temos que usar o método predicate

func recuperar() {
    //...

    //Recuperacao apenas do nome EXATO passado como argumento
    let predicteEqual = NSPredicate(format: "nome == %@", "Fábio França")

    //Recuperacao de todos os dados que contem o argumento ([c]-> nao é case sensitive)
    let predicateContains = (format: "nome contains [c] %@", "fábio")

    //Diferente do contains por questao de apenas começar com o argumento
    let predicateBeginswith = (format: "nome beginswith [c] %@", "fábio")

    //Operadores de comparacoes de numeros 
    let predicateCompare = (formart: "idade >= %@", "20")
    
    requisicao.predicate = predicteEqual
    //...
}

Para combinar filtros, temos que criar uma constante/variavel do tipo NSCompoundPredicate que contém alguns parâmetros que se adapta para cada regra de negócio ou de resolução do seu problema.

func recuperar() {
    //...

    //Combinacao com AND logico
    let combinacaoFiltroAnd = NSCompoundPredicate(andPreficateWithSubpredicates: [predicateContains,predicateCompare])

    //Combinacao com OR logico
    let combinacaoFiltroAnd = NSCompoundPredicate(orPreficateWithSubpredicates: [predicateContains,predicateCompare])

    //Combinacao com NOT logico
    let combinacaoFiltroAnd = NSCompoundPredicate(notPreficateWithSubpredicates: [predicateContains,predicateCompare])  

    //...
}

Depois basta passar a referência dessa combinação para o método predicate para recuperar os dados com esse tipo de filtro.

Atualizando dados do CoreData (Update)

Para atualizar dados utilizando o CoreData, precisamos buscar primeiro a instância que está persistida e atualizá-la.

Então, vamos primeiro criar nossa requisição e filtrar pelo nome mantendo nosso código modularizado conforme abaixo na função atualizar.

func atualizar() {
    let requisicao = NSFetchRequest<NSFetchRequestResult>(entityName: "Usuario")

    let predicateContains = NSPredicate(format: "nome contains [c] %@", "fábio")


    requisicao.predicate = predicateContains
}

Agora vamos criar o bloco de código do/catch para recuperar os dados.

func atualizar() {
    //...

    do {
        let usuarios = try context.fetch(requisicao)
        if usuarios.count > 0 {
            for usuario in usuarios as! [NSManagedObject]{
                // ...
            }
        }
        else{
            print("nenhum usuario encontrado")
        }
    } 
    catch  {
        print("Erro ao recuperar os usuarios")
    }
}

Nesse ponto já conseguimos atualizar os dados do usuário recuperado. Precisamos então atualizar os dados necessários.

Como filtramos no código acima, o usuário recuperado será o Fábio e queremos atualizar a sua idade para 25 anos. Então vamos atualizar os dados conforme o seguinte código:

func atualizar() {
    //...

    do {
        let usuarios = try context.fetch(requisicao)
        if usuarios.count > 0 {
            for usuario in usuarios as! [NSManagedObject]{
                usuario.setValue(25, forKey: "idade")

                //...
            }
        }
        else{
            print("nenhum usuario encontrado")
        }
    } 
    catch  {
        print("Erro ao recuperar os usuarios")
    }
}

Pronto, já atualizamos a sua idade, porém ainda não salvamos os dados. Basta então salvar os dados com o método save do context.

func atualizar() {
    //...

    do {
        let usuarios = try context.fetch(requisicao)
        if usuarios.count > 0 {
            for usuario in usuarios as! [NSManagedObject]{
                usuario.setValue(25, forKey: "idade")

                do {
                    try context.save()
                    print("dados atualizados com sucesso")
                }
                catch {
                    print("Erro ao atualizar os dados do usuário")
                }
            }
        }
        else{
            print("nenhum usuario encontrado")
        }
    } 
    catch  {
        print("Erro ao recuperar os usuarios")
    }
}

A função atualizar completa ficará assim:

func atualizar() {
    let requisicao = NSFetchRequest<NSFetchRequestResult>(entityName: "Usuario")

    let predicateContains = NSPredicate(format: "nome contains [c] %@", "fábio")

    requisicao.predicate = predicateContains

    do {
        let usuarios = try context.fetch(requisicao)
        if usuarios.count > 0 {
            for usuario in usuarios as! [NSManagedObject]{
                usuario.setValue(25, forKey: "idade")

                do {
                    try context.save()
                    print("dados atualizados com sucesso")
                }
                catch {
                    print("erro ao atualizar os dados do usuário")
                }
            }
        }
        else{
            print("nenhum usuario encontrado")
        }
    } 
    catch  {
        print("Erro ao recuperar os usuarios")
    }
}

Deletando dados do CoreData (Delete)

Para remover um dado precisamos inicialmente de selecionar esse dado. No nosso caso, vamos remover todos os usuários que contenham o nome Fabio. Para isso vamos criar uma requisição, fazer um filtro para que nossa requisição traga apenas os objetos que contenham o nome Fabio, e por último, um foreach para percorrer todos os objetos que vieram em nossa requisição.

Vamos criar novamente uma função específica para deletar os dados com nome deletar.

func deletar() {
   let requisicao = NSFetchRequest<NSFetchRequestResult>(entityName: "Usuario")

   let predicateContains = NSPredicate(format: "nome contains [c] %@", "fábio")

   requisicao.predicate = predicateContains

   do {
       let usuarios = try context.fetch(requisicao)
       if usuarios.count > 0 {
           for usuario in usuarios as! [NSManagedObject]{
               // ...
           }
       }
       else{
           print("nenhum usuario encontrado")
       }
   } 
   catch  {
       print("Erro ao recuperar os usuarios")
   }
}

Para realizar a exclusão, vamos precisar usar um método do nosso context passando como parâmetro o próprio objeto que queremos excluir.

func deletar() {
    let requisicao = NSFetchRequest<NSFetchRequestResult>(entityName: "Usuario")

    let predicateContains = NSPredicate(format: "nome contains [c] %@", "fábio")

    requisicao.predicate = predicateContains

    do {
        let usuarios = try context.fetch(requisicao)
        if usuarios.count > 0 {
            for usuario in usuarios as! [NSManagedObject]{
                context.delete(usuario)

                // ...
            }
        }
        else {
            print("nenhum usuario encontrado")
        }
    } 
    catch  {
        print("Erro ao recuperar os usuarios")
    }
}

Por último, precisamos salvar usando o método save do nosso objeto context.

func deletar() {
    let requisicao = NSFetchRequest<NSFetchRequestResult>(entityName: "Usuario")

    let predicateContains = (formart: "nome contains [c] %@", "fábio")

    requisicao.predicate = predicateContains

    do {
        let usuarios = try context.fetch(requisicao)

        if usuarios.count > 0 {
            for usuario in usuarios as! [NSManagedObject]{
                context.delete(usuario)

                do {
                    try context.save()
                    print("dados deletados corretamente!!!"  )
                } 
                catch {
                    print("erro ao deletar os dados")
                }
            }
        }
        else {
            print("nenhum usuario encontrado")
        }
    } 
    catch  {
        print("Erro ao recuperar os usuarios")
    }
}

Para finalizar, chame todas as funções no viewDidLoad para vê-las funcionando:

//...

override func viewDidLoad() {
    //...

    salvar()
    recuperar()
    atualizar()
    deletar()
}

//...

O código completo desse tutorial está disponível no GitHub. Basta acessar clicando aqui.

Autores: Murilo Teixeira, Fábio França.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment