-
[Swift] 다양한 데이터 타입 동적으로 Decoding 하기개발/Swift 2023. 1. 9. 17:55
위 데이터 처럼 하나의 리스트에 다른 타입의 데이터를 받아오는 경우가 있다.
이때 해당 데이터를 디코딩할때 문제를 겪게된다.
해결 할수 있는 방법은 위와 같이 모든 프로퍼티들을 옵셔널하게 주는 방법이 있다.
그러면 디코딩 한 결과는 옵셔널 하며 데이터 혹은 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값을 가져올수 있음을 볼수 있다.
이렇게 사용하면 된다.
'개발 > Swift' 카테고리의 다른 글
Rx + TableView(CollectionView) 바인딩 안되는 오류 해결방법 (0) 2023.03.21 [Swift] 직접 UITableView를 구현하기 (1) 2023.01.12 외부 영역 터치시 키보드 사라지게 하는방법 in TableView (0) 2022.12.05 Compositional Layout + Diffable DataSource - 2 헤더 추가하기 (0) 2022.11.27 [Swift] Compositional Layout - DiffableDataSource 와 함께 사용 (0) 2022.11.23