[Swift] JSON & Codable

Inwoo Hwang·2021년 8월 26일
0

Swift

목록 보기
4/8
post-thumbnail

Warning: 이해한 부분을 최대한 남기고 정리하려 남긴 글 입니다. 틀린 부분이 있을 수 있습니다. 이점 유의하고 읽어주시면 감사할 것 같습니다. 그리고 틀린 부분 알려주시면 바로바로 고치도록 하겠습니다.

Codable


Codable이 뭔가 했는데 Decodable& Encodable 프로토콜의 별칭(typealias)를 뜻하더라구요.

그러니까 Codable은 Decodable과 Encodable을 준수하는 프로토콜로 봐도 무방합니다.

Decodable: 자신을 외부표현(JSON)으로 인코딩할 수 있는 타입

Encodable: 외부 표현(JSON)으로부터 자신을 디코드 할 수 있는 타입

Read JSON File


JSON은 key와 value로 구성된 데이터이다.

  • 만약 파싱하려는 JSON 파일이 같은 종류의 요소들로 이뤄져있다면 Codable 또는 Decodable을 채택한 구조체를 활용하여 데이터를 읽어올 수 있다.

  • Codable을 채택한 구조체 전체에 해당되는 JSON 파일의 값을 파싱하고 싶을 때는 [구조체].self 를 사용하면 된다.

import Foundation

let json = """
[
    {
        "name": "Banana",
        "points": 200,
        "description": "A banana grown in Ecuador."
    },
    {
        "name": "Orange",
        "points": 100
    }
]
""".data(using: .utf8)!

struct GroceryProduct: Codable {
  var name: String
  var point: Int
  var description: String?
}

let decoder = Decoder()
let products = try decoder.decode([GroceryProduct].self, from: json)

구조체를 생성한 뒤 JSON에 해당되는 key에 매칭할 변수를 만들어 줘야 합니다. 변수의 이름과 key의 이름이 같아야 합니다.

Case 1: 만약 Asset폴더에 있는 JSON 파일을 파싱하려면?

NSDataAsset 는 Xcode의 모든 데이터 컨텐츠를 나타낸다. 그렇기 때문에 JSON파일이 Asset폴더에 저장되어있는 경우 해당 카탈로그로 접근해서 JSON파일을 가져온 뒤 파싱을 하면 된다.

접근 하기 위해서는 init?(name: ) 메서드를 통해 해당 data asset을 먼저 초기화 후 해당 데이터 참조 값을 갖는 object를 리턴 받아야 합니다

init?(name: )메서드는 name에 해당되는 데이터가 있으면 object를 반환하고 해당되는 데이터가 없을 경우 nil을 반환합니다.

아래와 같은 방법으로 파싱할 수 있겠습니다.

먼저 init메서드를 통해 반환 되는 값이 옵셔널 값이기 때문에 guard let 구문으로 옵셔널 추출을 해 준 뒤

let decoder = Decoder()
guard let asset = NSDataAsset.init(name: "파일이름") else { return }

let products = try decoder.decode([GroceryProduct].self, from: asset.data)

asset.data를 통해 asset폴더에 있는 데이터의 참조값을 가지고 있는 object의 데이터에 접근할 수 있다.

Change Key Names


JSON파일의 key name은 camelCase가 아니고 product_name 이런식으로 작성되는 경우가 많다. 그러면 구조체에서 매치하기 위해서는 camelCase를 쓰지 않고 var produce_name 이라고 선언 해 줘야 하는데 그러면 애플스타일이 아니지요! 이를 해결하기 위해서 CodingKey 를 활용하면 된다.

struct GroceryProduct: Codable {
    var name: String
    var points: Int
    var description: String?
    
    private enum CodingKeys: String, CodingKey {
        case name = "product_name"
        case points = "product_cost"
        case description
    }
}

스위프트 변수의 이름과 JSON key의 이름을 애플스타일로 매칭시켜주기 위해 구조체 안에 nested Enum을 활용할 수 있습니다.

Access Nested Data


아래와 같은 Nested JSON 데이터 같은 경우 어떻게 모델을 구성하고 Parsing을 할 수 있을까?

{
  "page": 1,
  "items": [
    {
      "id": 1,
      "title": "MacBook Pro",
      "price": 1690,
      "currency": "USD",
      "stock": 0,
      "thumbnails": [
        "https://camp-open-market.s3.ap-northeast-2.amazonaws.com/thumbnails/1-1.png",
        "https://camp-open-market.s3.ap-northeast-2.amazonaws.com/thumbnails/1-2.png"
      ],
      "registration_date": 1611523563.7237701
    }
  ]
}

먼저 해당 JSON에 매칭할 수 있는 아래와 같은 struct 모델을 구현하자.

struct MarketItemList: Decodable {
  let page: Int
  let items: [Items]
}

struct Items: Decodable {
  let id: Int
  let title: String
  let price: Int
  let currency: String
  let stock: Int
  let descriptions: String
  let discountedPrice: Int?
  let thumbnails: [String]
  let registrationDate: Double

  private enum CodingKeys: String, CodingKey {
    case id
    case title
    case price
    case currency
    case stock
    case descriptions
    case discountedPrice = "discounted_price"
    case thumbnails
    case registrationDate = "registration_date"
  }
}

더 복잡한 JSON파일을 파싱하는 방법을 아시고 싶으시면 아래 공식사이트 확인하시면 좋을 것 같습니다.↓

Using JSON with Custom Types | Apple Developer Documentation

[참고]:

Using JSON with Custom Types | Apple Developer Documentation

NSDataAsset | Apple Developer Documentation

init(name:) | Apple Developer Documentation

profile
james, the enthusiastic developer

0개의 댓글