티스토리 뷰

Mobile/iOS

Swift. Codable Decoding (1)

out of coding 2018. 6. 17. 12:25

Swift에서 Codable이라는 녀석이 생겼습니다.

기존에는 ObjectMapper등을 이용하여서 이것을 처리하였는데요.


ObjectMapper가 Warning이 발생하더니 더 이상 업데이트가 되지 않고 있었습니다.

더 이상 방치하고 싶지 않아서 이것을 사용해 보려고 하였습니다.

다른 프레임워크를 추가할 필요없이 끝나버렸습니다.


흡사 느낌은 거의 Gson과 거의 비슷한 느낌입니다.


기존의 ObjectMapper에서는 Mapping 하여주는 부분을 따로 지정을 하여 주어야 하는데, Reflection으로 처리가 되는것 같네요.


사용방법


1
2
3
4
5
struct SampleData: Codable {
    
    var name: String
    var age: Int
}
cs


이렇게 하고 Mapping만 하여 주면 끝납니다.


Test Code 입니다.


1
2
3
4
5
6
7
8
9
10
11
let data = """
{
    "name": "DH",
    "age": 100
}
""".data(using: .utf8)!
 
let sample = try! JSONDecoder().decode(SampleData.self, from: data)
print(sample) 
 
// Sample(name: "DH", age: 100)
cs


JSONDecoder를 이용하여서 손쉽게 만들수 있습니다.


그렇다면 Gson 처럼 DataClass에서 사용할 이름과 받아오는 이름이 다르다면?


DataStruct 안에 CodingKeys enum을 지정하여 주면 끝납니다.


1
2
3
4
5
6
7
8
9
10
struct SampleData: Codable {
    
    var name: String
    var age: Int
 
    enum CodingKeys: String, CodingKey {
        case name
        case age = "Age"
    }
}
cs


쉽죠. 창의력을 발휘하여서 JSon Array는 어떻게 하면 될까요?

답부터 말씀을 드리면 Array에 Codable을 넣어주면 됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct SampleData: Codable {
    
    var name: String
    var age: Int
    var friends: [Friend]
 
    enum CodingKeys: String, CodingKey {
        case name
        case age = "Age"
        case friends = "friend"
    }
}
 
struct Friend: Codable {
 
    var name: String
}
cs


기존에 ObjectMapper를 이용하시던 분들이면 이 부분도 쉽게 생각하고 끝났을것 같네요.


주의 할점은 CodingKeys를 지정하게 되면 조금 불편하겠지만 ObjectMapper 처럼 코드를 다 넣어주긴 해야합니다. ㅠㅠ


특정 Key가 옵션인 경우


만약 위에서 age는 옵션이라고 한다면 어떻게 처리하면 될까요?

그렇게 되면 자동으로 그냥 넘어가는게 아니라 에러가 발생하게 됩니다.

swift에서 struct, class는 변수를 초기화를 시켜주고 시작을 해야하는데 이러한 경우에는 초기화가 안되어 있기 때문입니다.


간단하게는 옵셔널로 처리하면 되긴 합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct SampleData: Codable {
    
    var name: String
    var age: Int?
    var friends: [Friend]
 
    enum CodingKeys: String, CodingKey {
        case name
        case age = "Age"
        case friends = "friend"
    }
}
 
struct Friend: Codable {
 
    var name: String
}
cs


이런 방법이 싫으신 분들을 위해서 코드를 더 넣으면 되긴 합니다. ㅎㅎ


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct SampleData: Codable {
    
    var name: String
    var age: Int
    var friends: [Friend]
 
    enum CodingKeys: String, CodingKey {
        case name
        case age = "Age"
        case friends = "friend"
    }
 
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decode(String.self, forKey: .name)
        age = (try? values.decode(Int.self, forKey: .age)) ?? 0
        friends = try values.decode(Friend.self, forKey: .friends)
    }
}
 
struct Friend: Codable {
 
    var name: String
}
cs


age는 값이 안들어올 경우에 try를 이용하여 nil이면 0이라는 기본값을 넣어주면 됩니다.

웬지 아래의 경우에는 이전 ObjectMapper와 비슷하게 사용해서 싫으네요 ㅎㅎ


특정 Key의 Value가 null인 경우


해결 방법 : 서버에서 null은 안내려주면 됩니다. ㅠ 솔찍히 따지자면. 저는 서버 개발할때 이러지는 않습니다.


그러나 이런 부분이 예외처리하는것이 서버에서 하기 힘들다는 이야기를 한다면 단말에서 개발해주어야겠죠.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct SampleData: Codable {
    
    var name: String?
    var age: Int
    var friends: [Friend]
 
    enum CodingKeys: String, CodingKey {
        case name
        case age = "Age"
        case friends = "friend"
    }
 
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decode(String.self, forKey: .name)
        age = try values.decode(Int.self, forKey: .age)
        friends = try values.decode(Friend.self, forKey: .friends)
    }
}
 
struct Friend: Codable {
 
    var name: String
}
cs


이전과 크게 다른 부분이 없어 보이기는 합니다. name은 optional로 지정이 되어 있습니다.

swift의 기본 구조상 optional이니 null이 들어갈수 있죠?


Array의 Key가 없는 Value만 존재하는 경우


위의 예제들에서 friends가 이름만 내려온다고 가정을 합시다... 이제 struct Friend는 잠시만 잊어주세요


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct SampleData: Codable {
    
    var name: String?
    var age: Int
    var friends: [String]
 
    enum CodingKeys: String, CodingKey {
        case name
        case age = "Age"
        case friends = "friend"
    }
 
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decode(String.self, forKey: .name)
        age = try values.decode(Int.self, forKey: .age)
        friends = try values.decode([String].self, forKey: .friends)
    }
}
cs


이렇게 타입을 [String]으로 만들어 주면 됩니다.


튜플과 같은 데이터 처리


이렇게... 데이터를 내려주지는 않습니다만... 이런 경우가 있다고 해서 정리를 해볼랍니다.


이렇게 하면 Key가 없는 값들의 자신의 값을 찾아서 빙글빙글돌면서 값을 넣어준다고 하네요.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct SampleData: Codable {
    
    var name: String?
    var age: Int
    var friends: [String]
 
    enum CodingKeys: String, CodingKey {
        case name
        case age = "Age"
        case friends = "friend"
    }
 
    init(from decoder: Decoder) throws {
        let container = try decoder.unkeyedContainer()
        name = try container.decode(String.self)
        age = try container.decode(Int.self)
        friends = try container.decode([String].self)
    }
}
cs


ObjectMapper를 사용하지 않아서 한결 좋은 코딩이 될것 같네요.


지금 개발된것에서 DataStruct들을 정리하면서 위의 내용들을 한번 해볼까 고민중이긴 합니다.


'Mobile > iOS' 카테고리의 다른 글

RxSwift. Transforming Observable  (0) 2018.06.19
Swift. Codable Encoding (1)  (0) 2018.06.17
RxSwift DisposeBag  (0) 2018.06.13
RxSwift distinct operator  (0) 2018.06.11
RxSwift의 take operators  (0) 2018.06.11
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함