문제
커서 기반 페이지네이션 구현 중 next_cursor 값을 디코딩하면서 오류가 발생하였다.
next_cursor로 다음 페이지를 계속해서 요청하다가 더이상 다음 데이터가 존재하지 않으면 0값을 가지고 온다.
응답 결과 디코딩 시 결과 값을 담을 구조체에 next_cursor 값의 타입이 두가지 경우가 존재하는 것이다.
String 값으로 정의하였더니 다음과 같은 TypeMismatch 오류가 발생하였다.
Swift.DecodingError.typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "next_cursor", intValue: nil)], debugDescription: "Expected to decode String but found number instead.", underlyingError: nil))
해결
이러한 문제를 해결하기 위해서는 결과 값이 제대로 디코딩이 되는지 확인 후 값을 변경시켜주는 방법이 있다. 응답 받을 구조체 모델에서 init 구문을 통해 확인하고 값을 변경해줄 수 있다.
1. init(decoder: )를 통해 타입 체크하기
CodingKey로 구성된 키 값을 사용하여 값을 디코딩할 때 문제가 발생하면 typeMismatch 에러를 throw를 하도록 정의되어있다.
struct ReadResponse: Codable {
let data: [Post]
var nextCursor: String
enum CodingKeys: String, CodingKey {
case data
case nextCursor = "next_cursor"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.data = try container.decode([Post].self, forKey: .data)
if let nextCursor = try? container.decode(String.self, forKey: .nextCursor) {
self.nextCursor = nextCursor
} else {
self.nextCursor = "0"
}
}
}
try 문을 통해 String 타입으로 디코딩이 되는지 확인하고 String으로 변환되지 않았을 때 String타입의 “0”을 저장하도록 구현하였다.
이 방법의 단점이라하면 모든 값들을 디코딩을 해야한다는 문제가 있다.
디코딩 해야하는 요소들의 개수가 많고, 타입 체크를 해야하는 요소가 적다면 불필요한 코드를 많이 작성하게 된다.
이러한 부분을 해결하기 위해 PropertyWrapper를 사용하여 하나의 요소만 확인하는 방식을 사용하였다.
2. @PropertyWrapper & .singleValueContainer()
@propertyWrapper
struct NextCursorType {
var wrappedValue: String
}
extension NextCursorType: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let nextCursor = try? container.decode(String.self) {
wrappedValue = nextCursor
} else {
wrappedValue = "0"
}
}
}
NextCursorType이라는 PropertyWrapper를 생성하였다.
위에서 init을 통해 디코딩 한 것과 비슷한 방식이다.
단일 값을 디코딩하는 메서드이고, container와 마찬가지로 에러 발생 시 typeMismatch를 thorw하도록 정의되어 있다.
wrappedValue의 값을 체크하여 String 타입이 아닐 시 String 타입의 “0”을 wrappedValue 값으로 설정한다.
struct ReadResponse: Codable {
let data: [Post]
@NextCursorType var nextCursor: String
enum CodingKeys: String, CodingKey {
case data
case nextCursor = "next_cursor"
}
}
길었던 init구문은 필요가 없게 되었고, NextCursorType이라는 PropertyWrapper를 통해 간단하게 값을 변경할 수 있다!!
'iOS > 🚨 오류 그리고 해결' 카테고리의 다른 글
[iOS/RxSwift] API 통신에서 Single과 Observable (0) | 2023.11.14 |
---|---|
[iOS/Swift] search bar 에 테두리와 그림자 동시 적용하기 - clipsToBounds (0) | 2023.10.01 |
[iOS/Swift] Modal Style과 LifeCycle (0) | 2023.09.07 |
테이블 뷰 셀 오류 (0) | 2023.08.03 |
멀고도 험한 AutoLayout 설정의 길 (0) | 2023.08.02 |