Swift 4 - Codable / JSONDecoder / JSONEncoder
개인적으로 꼽는 Swift 4의 가장 유용한 업데이트 중 하나로 Codable 프로토콜 및 JSONDecoder / JSONEncoder 를 꼽고 싶다. JSON 이라는 이름이 가지는 의미야 유명하니 무슨 말인지는 다들 알 것인데, Codable 이라는 프로토콜은 타입과 JSON을 언어 차원에서 묶어주는 방법을 제공해 준다는 점에서 파격적(?)이다.
이 글은 이 새로운 기능에 대해 겉(?)만 핥으려는 내용이다.
이제 이 타입에 맞는 JSON 데이터를 가지고 놀아볼 차례다.
하지만 Swift 4 부터는 Codable 을 따르는 구조체는 JSONDecoder 를 이용해 바로 분해해서 정리하는 것이 가능하다. Codable은 일종의 JSON 설계도 프로토콜인 셈이다.
쉽게 해결하고자 한다면 구식(?) 방법인 JSONSerialization 을 이용하자.
사실 Xcode 9 Beta 로 실습해 보면서 글을 정리하고 있는데 이유를 모르게 계속 죽어나간다. 뭔가 해보기가 너무 힘드니 여기서 마무리 해야겠다. ;-)
[관련글] 눈에 띄는 Swift 4 변경점들 (Xcode 9 첫 Beta 기준)
[관련글] 스위프트(Swift) 가이드
이 글은 이 새로운 기능에 대해 겉(?)만 핥으려는 내용이다.
주의: 이 글은 Xcode 9 Beta 버전 기준으로 작성되었다. 추후 정식 릴리즈 때 변경될 가능성이 있다.
Codable
사실 Codable 은 Encodable 과 Decodable 을 합쳐놓은 프로토콜이다. 하지만 이 둘을 별개로 알지 않아도 간단하게 쓰는데는 무리가 없다.우선 예제부터 보자. 아래는 Codable 프로토콜을 구현하는 MyType 이라는 구조체를 정의하는 코드이다.
struct MyType: Codable { let name: String let value: Int let hasJob: Bool let secondName: String? }여러가지 확인해 보고 싶어서 다양한 타입과 옵셔널도 집어 넣었다.
이제 이 타입에 맞는 JSON 데이터를 가지고 놀아볼 차례다.
JSONDecoder
Swift 3.x 이하에서는 JSON 데이터를 풀어헤치기(?) 위해서 JSONSerialization (Objective-C 라면 NSJSONSerialization) 이라는 클래스를 이용해 사전형 등의 표준 타입(?)으로 변경한 후 이를 다시 원하는 클래스나 구조체에 수동(?)으로 넣어주는 귀찮은 짓을 해야 했다.하지만 Swift 4 부터는 Codable 을 따르는 구조체는 JSONDecoder 를 이용해 바로 분해해서 정리하는 것이 가능하다. Codable은 일종의 JSON 설계도 프로토콜인 셈이다.
let inputData1 = """ { "name": "Tomato", "value": 100, "hasJob": false, "secondName": "is not fruit" } """.data(using: .utf8) let decoded1 = try JSONDecoder().decode(MyType.self, from: inputData1!) decoded1.name // "Tomato" decoded1.value // 100 decoded1.hasJob // false decoded1.secondName // "is not fruit"decoded1 은 MyType 형식의 값(Value)으로 생성된다. 대충 봐도 '뭐야 이거 물건이잖아!' 라는 소리가 나온다.
참고: Swift 4 부터 새로운 문자열 표현 방법으로 """...""" 문법이 추가되었다. 멀티 라인 문자열을 정의할 수 있는 문법으로 시작 """ 의 다음 줄에서 문자열이 시작되고, 끝 """ 의 윗 줄에서 문자열이 끝난다. 참고로 Python 에도 이와 비슷한 문법이 있다.옵셔널 필드인 secondName 을 정확히 테스트 하지 못 했기 때문에 한가지 더 시험해 보자.
let inputData2 = """ { "name": "Onion", "value": 200, "hasJob": false } """.data(using: .utf8) let decoded2 = try JSONDecoder().decode(MyType.self, from: inputData2!) decoded2.secondName // nil일부러 입력 데이터인 inputData2 문자열에서 secondName 필드를 빼버렸다. 이렇게 하면 디코드 시 알아서 해당 부분을 옵셔널 nil 로 치환해 주는 것을 확인할 수 있다.
JSONEncoder
이제 반대로 MyType 을 JSON으로 인코딩 하려면 JSONEncoder 를 사용 할 수 있다. 아래는 앞의 예제에서 계속 이어지는 코드 예제이다.let outputData2 = try JSONEncoder().encode(decoded2) let outputString2 = String(data: outputData2, encoding: .utf8)outputString2 의 내용을 굳이 적어야 할 필요는 없을 것 같다. 왜냐하면 위의 inputData2 의 내용과 동일하기 때문이다.
이상한 타입(?)을 쓰면 어떻게 될까
제목은 적당한 표현 방법이 없어서 이렇게 적긴 했는데, 그냥 아래 예제를 보는게 좋을 것 같다. 위에서 정의한 MyType 에 하나의 프로퍼티를 더 추가했다.struct MyType: Codable { let name: String let value: Int let hasJob: Bool let secondName: String? let userdata: [AnyHashable : Any] }마지막의 userdata 라는 사전형 타입이 추가했다. 이 프로퍼티는 어찌보면 상당히 모호한 표현일거라 생각되는데, 역시나 Codable 에서 오류를 내뱉는다. 그것도 두 가지다.
Type 'MyType' does not conform to protocol 'Decodable'
- Protocol requires initializer 'init(from:)' withType 'Decodable' (Swift.Decodable)
- Cannot automatically synthesize 'Encodable' because '[AnyHashable : Any]' does not conform to 'Encodable'
Type 'MyType' does not conform to protocol 'Encodable'뭔가 복잡해 보이는 이 에러 메시지는 사실 단순하다. userdata 라는 프로퍼티가 Encodable 및 Decodable 프로토콜을 따르지 않는다는 이야기다. 따라서 이 부분을 구현하면 해결은 가능하겠지만, 이는 왠지 배보다 배꼽이 더 커지는 것 같아 이번 글에서는 포기한다. ;;;;;
- Protocol requires function 'encode(to:)' with type 'Encodable' (Swift.Encodable)
쉽게 해결하고자 한다면 구식(?) 방법인 JSONSerialization 을 이용하자.
마무리
혁명(?)같다. 이런 것이 등장했다는 것은 최근 한국 정치사의 쾌거(?) 다음으로 기쁜 일 같다.사실 Xcode 9 Beta 로 실습해 보면서 글을 정리하고 있는데 이유를 모르게 계속 죽어나간다. 뭔가 해보기가 너무 힘드니 여기서 마무리 해야겠다. ;-)
[관련글] 눈에 띄는 Swift 4 변경점들 (Xcode 9 첫 Beta 기준)
[관련글] 스위프트(Swift) 가이드
댓글