ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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값을 가져올수 있음을 볼수 있다.

    이렇게 사용하면 된다.

    댓글

Designed by Tistory.