[Swift5] NSNumber의 Bool과 Int의 판정(Alam ofire)

17130 단어 SwiftAPIAlamofiretech

배경.


API 통신에 Alamofire를 사용하는 경우
파라메터(body)를 [string:Any]로 변환해서 건네드려야 합니다.
그때
  • Codable 인코딩을 통해 데이터 형식으로 변환
  • JsonSerialization을 사용하여 데이터로 변환→[string:Any]
  • 를 참고하십시오.
    예컨대 아래의 예.
    struct Todo: Codable {
      let id: String
      let title: String
      let number: Int
    }
    
    let todo: Todo = Model(id: "ssss", title: "hogehoge", number: 8)
    let data: Data = JSONEncoder().encode(todo)
    let parameter: [String:Any] = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
    print(parameter)
    // jsonに変換
    // ["id": "ssss", "title": "hogehoge", "number": 8]
    
    모델에는 String 및 Int만 있으면 위 절차에서 변환할 수 있습니다.
    문제는 아래처럼 볼형에 들어갈 때.
    struct Todo: Codable {
      let id: String
      let title: String
      let number: Int
      let isDone: Bool
    }
    
    let todo: Todo = Model(id: "ssss", title: "hogehoge", number: 8, isDone: true)
    let data: Data = JSONEncoder().encode(todo)
    let parameter: [String:Any] = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
    print(parameter)
    // jsonに変換
    // ["id": "ssss", "title": "hogehoge", "number": 8, "isDone": 1]
    // isDoneがfalseなら0,trueなら1に変換されてしまう
    
    앞에서 말한 바와 같이 부울형은 인트형으로 전환되어 원래 값이 부울인지 인트인지 판별할 수 없는 문제에 부딪혔다.

    해결책


    결론적으로 말하자면, 상술한 절차를 거친 후CFBooleanGetTypeIDCFGetTypeID를 취득하고 그 값을 비교함으로써 원래의 값이 Bool인지 Int인지 판정한다.
    코드는 아래와 같다.
    func isBoolNumber(number: NSNumber) -> Bool {
        let boolID = CFBooleanGetTypeID()
        let numID = CFGetTypeID(number)
        // 元の値がboolだとboolIDとnumIDが異なる値になる
        return numID == boolID
    }
    
    이 성립의 이유는 Json Serialize가 변환된 후의 값에 차이가 있기 때문이다.
    위의 변환 후number 및 isDone 유형 및 값에 주목number: NSNumber 8 isDone: NSNumber YES원래 Bool 값이었다면,
    TimeNSNumber YES휴가NSNumber NO이렇게
    이쪽 차이를 이용하면 위와 같이 다양한 ID를 얻으면 값이 달라져요.
    판정할 수 있다.

    전역 코드


    모델에 디렉터리라는 변수를 준비해서 변환할 복제자를 정의했습니다.
    struct Todo: Codable {
        let id: String
        let title: String
        let number: Int
        let isDone: Bool
    
        var dictionary: [String: Any]? {
            guard let data = try? JSONEncoder().encode(self) else { return nil }
            return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { data in
                if let datas = data as? [String: Any] {
                    return convertNSNumberToBool(value: datas)
                }
                return data as? [String: Any]
            }
        }
        // Boolの場合はBoolに変換を行う
        func convertNSNumberToBool(value: [String: Any]) -> [String: Any] {
            let array = value.mapValues { value -> Any in
    	    // 値がNSNumberの場合は判定の関数を行う
                if value is NSNumber {
                    if isBoolNumber(number: value as! NSNumber) {
                        return value as! Int == 0 ? false : true
                    }
    	    // jsonの階層は2階層以上の場合に対応
                } else if value is [String: Any] {
                    return convertNSNumberToBool(value: value as! [String: Any])
                }
                return value
            }
            return array
        }
        // NSNumberがboolかintか判定を行う
        func isBoolNumber(number: NSNumber) -> Bool {
            let boolID = CFBooleanGetTypeID()
            let numID = CFGetTypeID(number)
            return numID == boolID
        }
    }
    
    위의 방법
  • Codable 인코딩
  • Json Serialize를 통해 json
  • 으로 변환
  • 맵 함수를 통해 json의 데이터를 하나하나 판정하고 다시 대입
  • 이렇게 변환된 수치를 한 차례 강제로 수정했기 때문에 똑똑한 방법은 아니다.
    조사를 많이 했지만 이런 방법이 아니면 할 수 없었어요
    더 똑똑한 방법이 있다면 꼭 알려주세요.

    좋은 웹페이지 즐겨찾기