[SwiftUI] Codable

Junyoung Park·2022년 8월 20일
0

SwiftUI

목록 보기
21/136
post-thumbnail

Codable, Decodable, and Encodable in Swift | Continued Learning #21

Codable

구현 목표

  • Codable 프로토콜의 의미를 파악한다.
  • JSON 데이터를 디코딩한다.
  • 데이터를 JSON 데이터로 인코딩한다.

구현 태스크

  1. Codable을 사용하지 않은 상태에서의 JSON 디코딩/인코딩
  2. COdable 프로토콜을 사용한 JSON 디코딩/인코딩

핵심 코드

// struct CustomerModel: Identifiable, Decodable, Encodable {
// ...
// }

struct CustomerModel: Identifiable, Codable {
...
}

func getDataDecoder() {
        guard let data = getJSONData2() else { return }
        
        do {
            self.customer = try JSONDecoder().decode(CustomerModel.self, from: data)
        } catch {
            print(error.localizedDescription)
        }
    }
    

소스 코드

import SwiftUI

// DOWNLOAD DATA FROM NETWORK AS JSON
// JSON CONVERT -> DATA WE USE

struct CustomerModel: Identifiable, Decodable, Encodable {
    let id: String
    let name: String
    let points: Int
    let isPremium: Bool
    
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case points
        case isPremium
    }

    init(id: String, name: String, points: Int, isPremium: Bool) {
        self.id = id
        self.name = name
        self.points = points
        self.isPremium = isPremium
    }

    // DECODABLE
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(String.self, forKey: .id)
        self.name = try container.decode(String.self, forKey: .name)
        self.points = try container.decode(Int.self, forKey: .points)
        self.isPremium = try container.decode(Bool.self, forKey: .isPremium)
    }
    
    // ENCODABLE
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(name, forKey: .name)
        try container.encode(points, forKey: .points)
        try container.encode(isPremium, forKey: .isPremium)
    }
}

class CodableViewModel: ObservableObject {
    @Published var customer: CustomerModel? = nil
    
    init() {
        getDataDecoder()
    }
    
    func getDataDictionary() {
        guard let data = getJSONData() else { return }
        let jsonString = String(data: data, encoding: .utf8)
        print(jsonString)
//        Optional("{\"isPremium\":true,\"name\":\"Henry\",\"id\":\"12345\",\"points\":5}")
        
        if
            let localData = try? JSONSerialization.jsonObject(with: data),
            let dictionary = localData as? [String:Any],
            let id = dictionary["id"] as? String,
            let name = dictionary["name"] as? String,
            let points = dictionary["points"] as? Int,
            let isPremium = dictionary["isPremium"] as? Bool {
            
            let newCustomer = CustomerModel(id: id, name: name, points: points, isPremium: isPremium)
            customer = newCustomer
        }
        // getData as Dictionary
    }
    
    func getDataDecoder() {
        guard let data = getJSONData2() else { return }
        
        do {
            self.customer = try JSONDecoder().decode(CustomerModel.self, from: data)
        } catch {
            print(error.localizedDescription)
        }
    }
    
    func getJSONData() -> Data? {
        // DOWNLOAD FROM NETWORK
        // ASNYC -> ESCAPING CLOSURE OR AWAIT, ETC.
        
        let dictionary: [String:Any] = [
            "id" : "12345",
            "name" : "Henry",
            "points" : 5,
            "isPremium" : true,
        ]
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: dictionary)
            return jsonData
        } catch {
            print(error.localizedDescription)
        }
        return nil
    }
    
    func getJSONData2() -> Data? {
        let customer = CustomerModel(id: "111", name: "Amy", points: 30, isPremium: false)
        let jsonData = try? JSONEncoder().encode(customer)
        return jsonData
    }
}

struct CodableBootCamp: View {
    @StateObject private var viewModel = CodableViewModel()
    var body: some View {
        VStack(spacing: 20) {
            if let customer = viewModel.customer {
                Text(customer.id)
                Text(customer.name)
                Text("\(customer.points)")
                Text(customer.isPremium.description)
            }
        }
    }
}
  1. Encodable, Decodable을 사용하지 않고 JSON 데이터 읽고 쓰기: [String:Any]로 JSON 만들기 → JSON 오브젝트를 [String:Any] 딕셔너리로 캐스팅 → 프로퍼티 가져와서 새로운 구조체 이니셜라이즈
  2. Decodable 사용: 기존 구조체 생성자 이외에 Decodable을 사용하는 생성자 선언. 구조체의 프로퍼티를 코딩 키로 사용하는 컨테이너 구현 → JSON 디코더 사용
  3. Encodable 사용: 구조체 함수 encode (Encodable 프로토콜을 준수하는 함수)에 코딩 키에 대한 구조체 프로퍼티 넣기 → JSON 인코더 사용
  4. Codable 사용: Encodable, Decodable 모두 사용, 코딩 키와 함께 Decodable 위한 생성자 및 encdoe 함수 모두 자동으로 생성(커스텀 가능).
profile
JUST DO IT

0개의 댓글