Swift 4 – Serializare

Aplicațiile IOS au permanentă nevoie de comunicare peste rețea, fie către un server local sau internet.
Dacă până la SWIFT 4 lucrurile erau pre puțin standardizate, surpiza făcută de Apple este pe măsura așteptărilor.

Să începem discuția.

O aplicație IOS va trimite peste rețea date în format binar sau text. Pentru a realiza acest lucru are nevoie de convertirea instanțelor din aplicația locală într-un format diferit care este acceptat de mașina destinație. Acest proces de conversie a informațiilor locale în format inteligibil de către un server se numește SERIALIZARE. Apple propune un mecanism de encoding pentru a realiza această conversie și un mecanism invers de conversie a răspunsului venit de la server în informație inteligibilă în aplicație, numit decoding.

Protocoale necesare: Encodable & Decodable & Codable

Pentru a conforma o clasă la Codable trebuie ca toate proprietățile sale să fie în prealabil Codable.
Tipurile standard: Float, Int, String, Data, Date, URL sunt conforme cu Codable, prin urmare structura următoare este explicit Codable, suportând metodele init(from:) și encode(to:).

struct Pescar:Codable {
    var nume: String
    var numarPermis: Int
}

Trebuie să specificăm faptul că tipurile Array, Dictionary sau Optional sunt Codable dacă ele conțin tipuri de date Codable.
Un mic exemplu în acest sens:

let pescarIncepator = Pescar(nume: "Danut", numarPermis: 1)
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(pescarIncepator)
print("JSON venit din Codable: /(jsonData)")
// JSON venit din Codable: 10 bytes

jsonData reprezintă formatul binar pe care-l va primi serverul și pe care-l va transforma ulterior în JSON-ul așteptat.
Dacă încercăm să afișăm conținutul jsonData vom obține de fapt doar o dimensiune a binarului care conține JSON-ul.
“Miezul” jsonData-ului poate fi afișat sub forma:

let jsonString = String(data: jsonData, encoding: .utf8)
print("JSON venit din Codable: /(jsonString)")
// JSON venit din Codable: {"nume":"Danut","numarPermis":1}

Raspunsul serverului trebuie decodificat pe cale inversă, folosind decode(_:from:)

let jsonDecoder = JSONDecoder()
let pescarAvansat = try jsonDecoder.decode(Pescar.self, from: jsonDataResponse)
// JSON venit din Codable: {"nume":"Dorulet","numarPermis":2}

Observații
1.jsonDataResponse este raspunsul furnizat de server ca urmare a unui request furnizat anterior.
2. decoder-ul are nevoie să cunoască spre ce tip va converti JSON-ul prin jsonDataResponse

Situație critică – în backend s-a modificat structura tabelei asociată pescarilor noștri astfel încât în acest moment câmpul numarPermis a fost redenumit în idPermis. Cum se poate gestiona o astfel de situație?

Swift 4 vine cu un protocol nou CodingKey și cu un enumeration special CodingKeys.
Ce avem de făcut astfel încât să serializăm instanța noastră de tip Pescar pentru ca pe server JSON-ul să conțină idPermis?
Vom modela un pic struct-ul nostru:

struct Pescar:Codable {
    var nume: String
    var numarPermis: Int
    
    enum CodingKeys: String, CodingKey {
        case nume 
        case numarPermis = "idPermis"
        
    }
}

Prin urmare,

let pescarIncepator = Pescar(nume: "Danut", numarPermis: 1)
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(pescarIncepator)

let jsonString = String(data: jsonData, encoding: .utf8)
print("JSON venit din Codable: /(jsonString)")
// JSON venit din Codable: {"nume":"Danut","idPermis":1}

Share Button

Swift 3 – JSON – extragere informatii dintr-un fisier si transformare in Data

Avem urmatorul scenariu. Un fisier local ServerData.json care serveste continut pentru aplicatie; e o situatie putin atipica, in general content-ul vine din exterior.

Revenim, fisierul ServerData.json este adaugat in proiectul nostru si are in Target membership – File Inspector activat numele proiectului pentru a fi inclus la compilare.

Pentru a extrage informatiile din JSON si a le transforma in Data definim urmatoarea metoda:

 

public func dataFromFile(_ filename: String) -> Data? {

    @objc class TestClass: NSObject { }

    let bundle = Bundle(for: TestClass.self

    if let path = bundle.path(forResource: filename, ofType: “json”) {

        return (try? Data(contentsOf: URL(fileURLWithPath: path)))

    }

    return nil

}

bundle va fi un “container” care va incarca din path fisierul cu extensia json in caz ca exista (if let) urmand a obtine content-ul in format Data al json-ului.

 

Share Button