JSON 는 자바스크립트에서 객체를 표현하는 방식으로 REST API 에서 널리 사용되는 방식입니다.
iOS 에서는 Codable 을 통해 쉽게 JSON 을 디코딩할 수 있는데요.
Codable 과 Dictionary 로 디코딩 하는 방법을 살피고 예외적인 상황까지 이번 포스팅에서 다루고자 합니다.
Codable
Codable 은 Decodable & Encodable 프로토콜을 모두 준수하는 타입입니다.
따라서 Encode 할 필요가 없다면 Decodable 만 채택해도 네트워킹 처리에는 문제가 없습니다.
Decodable 는 프로토콜이기 때문에 Class, Struct, Enum 모두 채택할 수 있습니다.
import Foundation | |
struct TestResponse: Decodable { | |
let id: String | |
let image: Test | |
enum CodingKeys: String, CodingKey { | |
case id = "course_id" | |
case image | |
} | |
} | |
struct Test: Decodable { | |
let small: String | |
let large: String | |
} | |
let data = Data( | |
""" | |
{ | |
"course_id": "course-v1", | |
"image": { | |
"small": "http://www.kmooc.kr/asset-v1/small", | |
"large": "http://www.kmooc.kr/asset-v1/large" | |
} | |
} | |
""".utf8 | |
) | |
let testResponse = try! JSONDecoder().decode(TestResponse.self, from: data) | |
print(testResponse) |
추가적으로 CodingKeys 를 통해 Key 값을 변경할 수 있습니다.
Encode and Decode Manually
위 같이 JSON 요청과 모델의 구조가 일치하는 경우에는 직관적으로 Decoding 을 수행할 수 있습니다.
하지만 JSON 과 Model 의 구조가 다른 경우에는 커스텀하게 Decode 을 진행해야 합니다.
import Foundation | |
struct TestResponse { | |
let id: String | |
let small: String | |
let large: String | |
enum CodingKeys: String, CodingKey { | |
case id = "course_id" | |
case image | |
} | |
enum ImageKeys: String, CodingKey { | |
case small | |
case large | |
} | |
} | |
extension TestResponse: Decodable { | |
init(from decoder: Decoder) throws { | |
let keys = try decoder.container(keyedBy: CodingKeys.self) | |
id = try keys.decode(String.self, forKey: .id) | |
let imageKeys = try keys.nestedContainer(keyedBy: ImageKeys.self, forKey: .image) | |
small = try imageKeys.decode(String.self, forKey: .small) | |
large = try imageKeys.decode(String.self, forKey: .large) | |
} | |
} | |
let data = Data( | |
""" | |
{ | |
"course_id": "course-v1", | |
"image": { | |
"small": "http://www.kmooc.kr/asset-v1/small", | |
"large": "http://www.kmooc.kr/asset-v1/large" | |
} | |
} | |
""".utf8 | |
) | |
let testResponse = try! JSONDecoder().decode(TestResponse.self, from: data) | |
print(testResponse) |
기존 구조와 달리 small 과 large 프로퍼티가 모두 TestResponse 안으로 들어가도록 변경되었습니다.
이때 CodingKeys 안에는 JSON 과 같은 형태인 image case 와 ImageKeys 를 추가합니다.
그 후 코드 가독성을 위해 Extension 으로 Decodable 를 채택하고, 이니셜 라이저를 작성합니다.
먼저 가장 상위 Codingkeys 를 통해 직접 Decode 를 진행합니다.
그 다음 nestedContainer 를 통해 ImageKeys 에 접근하고 하위 프로퍼티들을 Decode 해주면 끝입니다.
jsonserialization 를 통한 디코딩
Codable 이 존재하지 않을 때 iOS 5 버전부터 지원한 방식입니다.
리턴 값이 딕셔너리 [String: Any] 값으로 각각 타입 캐스팅을 통해 모델을 구성해야 합니다.
import Foundation | |
struct TestResponse { | |
let id: String | |
let small: String | |
let large: String | |
static func parseTest(by jsonObject: [String: Any]) -> TestResponse { | |
let imageObject = jsonObject["image"] as! [String: Any] | |
return TestResponse( | |
id: jsonObject["course_id"] as! String, | |
small: imageObject["small"] as! String, | |
large: imageObject["large"] as! String | |
) | |
} | |
} | |
let data = Data( | |
""" | |
{ | |
"course_id": "course-v1", | |
"image": { | |
"small": "http://www.kmooc.kr/asset-v1/small", | |
"large": "http://www.kmooc.kr/asset-v1/large" | |
} | |
} | |
""".utf8 | |
) | |
let jsonObject = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] | |
let testResponse = TestResponse.parseTest(by: jsonObject) | |
print(testResponse) |
key 값으로 직접 접근할 수 있다는 장점이 있지만, 많은 타입 캐스팅 구문을 사용해야하기 때문에
레거시를 지원해야 하는 경우가 아니라면 Codable 를 사용하는 것이 좋을 것 같습니다.
참고 문서:
Apple Developer Documentation
developer.apple.com
Apple Developer Documentation
developer.apple.com
'iOS Dev > Swift' 카테고리의 다른 글
Swift Reference Count 고찰 ( with side table ) (0) | 2021.10.08 |
---|---|
지금까지 받았던 코드 리뷰 정리 ( iOS, Swift ) (0) | 2020.11.10 |
Swift Optional ( enum, wrapped ) 정리 (0) | 2020.10.12 |
Swift ARC ( Automatic Reference Counting ) 정리 (2) | 2020.10.07 |
윈도우 10 에서 Swift 5 사용하기 ( Visual Code, WSL ) (4) | 2020.08.01 |