24 out 2016

Trabalhando com dados Json em Swift

O JSON – JavaScript Object Notation, é um formato de dados utilizado para intercâmbio de dados computacionais que é empregado na maioria das aplicações atuais e é definido pela Json.org.

Existem no mercado vários frameworks, e para diversas linguagens, que ajudam na conversão de objetos Json para seu modelo de dados porém nesse post vamos focar em apenas um deles, o ObjectMapper disponível para swift.

Para começar vamos criar o nosso modelo de dados de um usuário de nosso sistema e que deverá ser enviado para nosso servidor.

class User {
    
    var id = 0
    var active = true
    var updateAt = Date()
    var name = String()
    var state: State?
    var cpf = String()

}

No código criamos um classe que representa o nosso usuário com seu nome, estado e cpf, também foi incluído controladores para saber qual identificador do usuário (id) qual a data da última modificação e se o mesmo está ativo no sistema.
Finalizado o mapeamento do nosso modelo agora vamos utilizar o framework para mapear o objeto para o formato Json e para isso primeiramente vamos importar a biblioteca no nosso arquivo modelo.

import ObjectMapper

Após realizar o import vamos criar uma extensão da classe User e assinar o protocolo “Mappable” do framework que será responsável pela conversão dos dados.

import Foundation
import ObjectMapper

class User {
    
    var id = 0
    var active = true
    var updateAt = Date()
    var name = String()
    var state: State?
    var cpf = String()

    required init?(map: Map) {

    }
}

// MARK: Mappable
extension User: Mappable {
    
    func mapping(map: Map) {
        
    } 
}

A implementação do protocolo Mappable exige a implementação do construtor, que por ser requerido não pode ficar na extensão, e da função mapping responsável pelo mapeamento do objeto. Agora basta mapear o objeto, vinculando cada propriedade do modelo com uma chave específica em Json, dentro da função mapping. Observe que temos no nosso modelo um objeto aninhado, o estado, que também deverá implementar o protocolo Mappable para realizar a conversão corretamente.

func mapping(map: Map) {

        id       <- map["id"]
        active   <- map["active"]
        updateAt <- map["updated_at"]
        name     <- map["name"]
        state    <- map["state"]
        cpf      <- map["cpf"]

}

Finalizado o mapeamento das propriedades do nosso modelo basta chamar a função "ToJson()", disponibilizada pela implementação do protocolo Mappable, que teremos o seguinte resultado:

{
  "id": 0,
  "active": true
  "updateAt": "Oct 12, 2015, 4:49 PM"  
  "cpf": "111.111.211-11",
  "state": {
    "id": 1
    "name": "São Paulo"
    }
}

Apesar do objeto estar correto do ponto de vista do protocolo JSON com relação aos nossos dados ainda temos alguns pontos a resolver, como por exemplo a data, que esta no formato padrão de impressão e que não é compatível com o formato aceito por nosso servidor.
Nesse caso precisamos não apenas mapear nossos dados mas também transforma-lo para o formato correto aceito pelo servidor que é: 2016-10-18T21:00:57.431-0200, e para isso o framework possui um protocolo que permite implementar a transformação chamado "Transform".

public protocol TransformType {
    associatedtype Object
    associatedtype JSON

    func transformFromJSON(_ value: Any?) ->; Object?
    func transformToJSON(_ value: Object?) ->; JSON?
}

No protocolo é possível ver que temos dois tipos associonados sendo Object o tipo do seu Modelo e JSON o tipo que será retornado após a transformação e duas funções responsáveis pela transformação propriamente dita.

Vamos criar uma estrutura chamada ServerDateTransform que assinar o protocolo para implementar as transformações necessárias. No nosso caso o tipo associado ao Object será o Date e ao JSON o resultado final será string (data convertida para o formato correto).

import Foundation
import ObjectMapper

struct ServerDateTransform: TransformType {
    
    public typealias Object = Date
    public typealias JSON = String
    
    public init() {}
    
    public func transformFromJSON(_ value: Any?) -> Date? {
        return nil
    }
    
    public func transformToJSON(_ value: Date?) -> String? {
        return nil
    }
    
}

Com nossa estrutura criada vamos implementar a transformação da data utilizando o DateTransform do iOS para imprimir a data no formato correto que precisamos, vamos criar uma variável computada que retorna um DataTransform com o formato desejado.

fileprivate var dateFormatter: DateFormatter {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
        
        return dateFormatter
    }

E por fim implementar as funções de transformações, resultando no seguinte código.

import Foundation
import ObjectMapper

struct ServerDateTransform: TransformType {
    
    public typealias Object = Date
    public typealias JSON = String
    
    public init() {}
    
    fileprivate var dateFormatter: DateFormatter {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
        
        return dateFormatter
    }
    
    public func transformFromJSON(_ value: Any?) -> Date? {
        if let strDate = value as? String {
            return dateFormatter.date(from: strDate)
        }
        
        return nil
    }
    
    public func transformToJSON(_ value: Date?) -> String? {
        if let date = value {
            return dateFormatter.string(from: date)
        }
        return nil
    }
    
}

Se atente para o fato que a função tem como parâmetro uma objeto nullable e nesse caso devemos sempre validar se a entrada é válida para evitar erros futuros.

Agora basta aplicar a nova estrutura na propriedade de data da função "mapping" do modelo para realizar a transformação correta.

func mapping(map: Map) {
        
        id       <- map["id"]
        active   <- map["active"]
        updateAt <- (map["updated_at"], ServerDateTransform())
        name     <- map["name"]
        state    <- map["crm_state_id"]
        cpf      <- map["cpf"]

}

Note que o operador <- pode receber apenas um map com a chave do JSON ou uma tupla com a chave e o transformação necessária.
Aplicando a transformação temos a seguinte saída:

{
  "id": 0,
  "active": true
  "updateAt": "2016-10-18T21:00:57.431-0200"  
  "cpf": "111.111.211-11",
  "state": {
    "id": 1
    "name": "São Paulo"
    }
}

Com isso podemos implementar qualquer transformação de dados necessário para comunicação entre nossos sistemas.

Leave a Comment