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.