210406 Tue

Sunny·2021년 4월 8일
0

Today I Learned

목록 보기
28/88

1. 첫 번째 학습 내용: Codable

Codable을 사용하면 decodable과 encodable 프로토콜을 둘다 적용할 수 있다.

만약 decodable만 쓸거라면 반드시 Codable을 써야 할 필요는 없다.

(decodable만 써도 되니까)

typealias Codable = Decodable & Encodable

Codable is a type alias for the Encodable and Decodable protocols.

When you use Codable as a type or a generic constraint, it matches any type that conforms to both protocols.

protocol Decodable
// A type that can decode itself from an external representation.

protocol Encodable
A type that can encode itself to an external representation.

Encodable -> data를 Encoder에서 변환해주려는 프로토콜로 바꿔주는 것

Decodable -> data를 원하는 모델로 Decode 해주는 것

위의 정의에서 external representation을 json이라고 예로 들자면,

Encodable -> 모델을 json으로 인코드

Decodable -> json을 내가 원하는 모델로 디코드

만국박람회 프로젝트의 경우 json 파일에 있는 내용을 모델 객체에 decode 해주는 것.

출처: Swift :: Codable 알아보기 by 상어

2. 두 번째 학습 내용: JSON Encoding and Decoding 실습

Encoding

import Foundation

struct Coffee : Codable {
    var coffeeName : String
    var coffeeprice : Int
}

let encoder = JSONEncoder()
encoder.outputFormatting = [.sortedKeys, .prettyPrinted]
let caffeLatte = Coffee(coffeeName: "카페 라떼", coffeeprice: 4000)
let jsonData = try? encoder.encode(caffeLatte)

if let jsonData = jsonData, let jsonString = String(data: jsonData, encoding: .utf8) {
    print(jsonString)
}

encoder.outputFormatting = [.sortedKeys, .prettyPrinted] 해주기 전 출력 모습

{"coffeeprice":4000,"coffeeName":"카페 라떼"}

encoder.outputFormatting = [.sortedKeys, .prettyPrinted] 해준 후의 출력 모습

→ sortedKeys는 알파벳 순으로 정렬

→ prettyPrinted는 줄바꿈을 해줘서 이쁘장하게 출력해준다.

{
  "coffeeName": "카페 라떼",
  "coffeeprice": 4000
}

Decoding

인코딩은 됐는데 디코딩이 안된다...
인코딩 값만 제대로 나오고 끝남 ㅠㅠ

예정에 없던 숨은 그림 찾기를 해보았다 ^.^

다음 중 잘못된 것은??

import Foundation

struct Coffee : Codable {
    var coffeeName : String
    var coffeeprice : Int
}

let encoder = JSONEncoder()
encoder.outputFormatting = [.sortedKeys, .prettyPrinted]
let caffeLatte = Coffee(coffeeName: "카페 라떼" coffeeprice: 4000)
let jsonData = try? encoder.encode(caffeLatte)

if let jsonData = jsonData, let jsonString = String(data: jsonData, encoding: .utf8) {
    print(jsonString)
}

let jsonString = """
{
    "coffeeName" : "카페 라떼"
    "coffeeprice" : 4000
}
"""

let decoder = JSONDecoder()
var data = jsonString.data(using: .utf8)
if let data = data, let myCoffee = try? decoder.decode(Coffee.self, from: data) {
    print(myCoffee.coffeeName)
    print(myCoffee.coffeeprice)
}
  • 정답

    안되는 이유를 Kane이 찾아줬다...

    jsonString 안에서 카페 라떼하고선 컴마를 빼먹었다 😅

    휴먼 에러를 구현할 계획이 없었는데

    몸소 보여줬다 ^.ㅠ

    다른 개선점은 카멜케이스를 적용하지 않았던 coffeeprice를 coffeePrice로 고쳐줬다.

    같이 고민해준 탁, 스티븐, 케인 고맙습니다 😉 👍

    import Foundation
    
    struct Coffee : Codable {
        var coffeeName : String
        var coffeePrice : Int
    }
    
    // string을 여러줄로 할때 """로 묶어줌
    let jsonString = """
    {
        "coffeeName" : "카페 라떼",
        "coffeePrice" : 4000
    }
    """
    
    let decoder = JSONDecoder()
    var data = jsonString.data(using: .utf8)
    
    if let data = data {
       if let myCoffee = try? decoder.decode(Coffee.self, from: data) {
        print(myCoffee.coffeeName) // print 카페 라떼
        print(myCoffee.coffeePrice) // print 4000
       }
    }

출처

Swift ) 왕초보를 위한 Codable / JSON Encoding and Decoding [ZeddiOS]

Encoding and Decoding Custom Types

  • Encode and Decode Automatically

  • Encode or Decode Exclusively

    Encode나 Decode 둘 중 필요한 것만 아래처럼 골라써라.

    struct Landmark: Encodable {
        var name: String
        var foundingYear: Int
    }
    struct Landmark: Decodable {
        var name: String
        var foundingYear: Int
    }
  • Choose Properties to Encode and Decode Using Coding Keys

    • Codable 타입은 특별히 타입 내부에서 쓸 수 있는 CodingKeys라는 이름의 열거형을 쓸 수 있음

    • CodingKeys 요 아이를 어떻게 쓰느냐?

      → 예를 들어 json 파일에는 foundingdate 이렇게 되어 있는데, swift에서는 언더바() 형식의 네이밍을 변수에서 안씀. 규칙에 위배됨?!

      → 그래서 json에서는 founding_date로 돼있지만, foundingYear로 바꿔서 쓰겠습니다~ 요런 느낌?!

      struct Landmark: Codable {
          var name: String
          var foundingYear: Int
          var location: Coordinate
          var vantagePoints: [Coordinate]
          
          enum CodingKeys: String, CodingKey {
              case name = "title"
              case foundingYear = "founding_date"
              
              case location
              case vantagePoints
          }
      }
  • Encode and Decode Manually

    Decodable를 extension 할때는 init 해주기

    extension Coordinate: Decodable {
        init(from decoder: Decoder) throws {
            let values = try decoder.container(keyedBy: CodingKeys.self)
            latitude = try values.decode(Double.self, forKey: .latitude)
            longitude = try values.decode(Double.self, forKey: .longitude)
            
            let additionalInfo = try values.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
            elevation = try additionalInfo.decode(Double.self, forKey: .elevation)
        }
    }

    Encodable 프로토콜을 extension하고 싶으면 encode(to:) 메소드를 써주면 됨

    extension Coordinate: Encodable {
        func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)
            try container.encode(latitude, forKey: .latitude)
            try container.encode(longitude, forKey: .longitude)
            
            var additionalInfo = container.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
            try additionalInfo.encode(elevation, forKey: .elevation)
        }
    }

출처

공식 문서 Encoding and Decoding Custom Types

profile
iOS Developer

0개의 댓글