[Swift] 다양한 데이터 타입 동적으로 Decoding 하기
위 데이터 처럼 하나의 리스트에 다른 타입의 데이터를 받아오는 경우가 있다.
이때 해당 데이터를 디코딩할때 문제를 겪게된다.
해결 할수 있는 방법은 위와 같이 모든 프로퍼티들을 옵셔널하게 주는 방법이 있다.
그러면 디코딩 한 결과는 옵셔널 하며 데이터 혹은 nil 이 들어갈것이다.
이런 경우 문제점은 프로퍼티가 모두 옵셔널 하기 때문에 언래핑을 꼭 해줘야 한다는것이다.
더 좋은 방식은 해당 타입을 나누어 다른 타입으로 구현해내는 것이다.
어느 방식으로든 분기를 태워서 A타입, B타입으로 나누어 필요한 프로퍼티만 설정해주는것이다.
enum CellItem: Decodable , Hashable{
case company(Company)
case horizontal(Horizontal)
case none
enum CodingKeys: String, CodingKey {
case type = "cell_type"
}
enum CellType: String, Codable {
case company = "CELL_TYPE_COMPANY"
case horizontal = "CELL_TYPE_HORIZONTAL_THEME"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
guard let type = try? container.decode(CellType.self, forKey: .type) else {
self = .none
return
}
switch type {
case .company:
self = try .company(Company(from: decoder))
case .horizontal:
self = try .horizontal(Horizontal(from: decoder))
}
}
}
그러기 위해서 우선 Enum 타입으로 정의했다. company , horizontal 타입두개를 정의했고
나머지는 none으로 처리할것이다.
Hashable은 diffabledatasource를 사용하기 위함이니 무시하자
init()함수 를 보자
우선 타입을 String 값을 보고 CellType이라는 enum타입으로 변환해준다.
switch 문을 보면 여기서 분기를 타서 enum value값을 넣어주었음을 볼수 있다.
이제 Company 타입과 Horizontal 이라는 Struct를 구현해줘야한다.
해당 타입들은 Enum 안에서 정의했다.
//CellItem
struct Company: Decodable, Hashable{
var cellType: CellType
var name: String
let logoPath: String
let industryName: String
let rateTotalAvg: Float
let reviewSummary: String
let interviewQuestion: String
let salaryAvg: Int
let updateDate: String
private enum CodingKeys: String, CodingKey {
case cellType = "cell_type"
case logoPath = "logo_path"
case name
case industryName = "industry_name"
case rateTotalAvg = "rate_total_avg"
case reviewSummary = "review_summary"
case interviewQuestion = "interview_question"
case salaryAvg = "salary_avg"
case updateDate = "update_date"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
cellType = try container.decode(CellType.self, forKey: .cellType)
logoPath = try container.decode(String.self, forKey: .logoPath)
name = try container.decode(String.self, forKey: .name)
industryName = try container.decode(String.self, forKey: .industryName)
rateTotalAvg = try container.decode(Float.self, forKey: .rateTotalAvg)
reviewSummary = try container.decode(String.self, forKey: .reviewSummary)
interviewQuestion = try container.decode(String.self, forKey: .interviewQuestion)
salaryAvg = try container.decode(Int.self, forKey: .salaryAvg)
updateDate = try container.decode(String.self, forKey: .updateDate)
}
}
struct Horizontal: Decodable , Hashable{
var cellType: CellType
var sectionTitle: String
let count: Int
var recommendRecruit: [RecruitItem]
private enum CodingKeys: String, CodingKey {
case cellType = "cell_type"
case count
case sectionTitle = "section_title"
case recommendRecruit = "recommend_recruit"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
cellType = try container.decode(CellType.self, forKey: .cellType)
count = try container.decode(Int.self, forKey: .count)
sectionTitle = try container.decode(String.self, forKey: .sectionTitle)
recommendRecruit = try container.decode([RecruitItem].self, forKey: .recommendRecruit)
}
}
각 타입에는 공통 프로퍼티인 cellType과 그외 각자가 가져야할 프로퍼티를 넣어줬다.
이러면 이제 어느 타입의 데이터가오든 CellItem이라는 데이터 리스트로 디코딩이 가능해진다.
그리고 CellItem의 타입이 company 혹은 horizontal 로 나뉠것이며 그 안에 value 가 담겨져있다.
private func filterData(items: [CellItem], searchText: String) -> [CellItem] {
let filtered = items.compactMap { item -> CellItem? in
switch item{
case .company(let companyItem):
let name = companyItem.name.lowercased()
if name.contains(searchText) {
return item
}
return nil
case .horizontal(let horizontalItem):
var temp = horizontalItem
temp.filterRecommendRecruit(text: searchText)
let recommendRecruit = temp.recommendRecruit
if recommendRecruit.isEmpty { return nil }
let cellItem = CellItem.horizontal(temp)
return cellItem
default:
return nil
}
}
return filtered
}
위 함수는 받아온 데이터에서 검색어가 포함된 데이터만 필터링하는 함수이다.
다른거 무시하고 switch 문을 보면 enum 타입을 활용해 분기를 타고 value값을 가져올수 있음을 볼수 있다.
이렇게 사용하면 된다.