Как обрабатывать два разных ответа JSON с помощью Alamofire

Я делаю функцию входа в систему в приложении SwiftUI. При успешном входе в систему ответ JSON:

    {
    "user_id": 41,
    "token": "Token",
    "token_type": "bearer",
    "expires_in": 12096000
    }

При неудачном входе в систему ответ JSON:

{
 "message": "this is a failure message"
}

Я сделал две разные структуры для кодирования ответов

struct LoginResponseModelFailure:Codable {
    let message:String
}

struct LoginResponseModelSuccess:Codable{
    let user_id: Int
    let token: String
    let token_type : String
    let expires_in: Int
}

Нужно ли мне объединить эти две структуры в одну? если да то как это сделать? Как я могу обрабатывать два разных ответа, используя alamofire или urlSession?


person tanvirgeek    schedule 26.11.2020    source источник
comment
Вы можете объединить их в один и сделать сообщение необязательным. Если это ноль, то успех, в противном случае - неудача   -  person cora    schedule 26.11.2020


Ответы (3)


Чтобы избежать опций, я предлагаю объявить корневой объект как перечисление со связанными типами.

enum Response : Decodable {
    case success(ResponseSuccess)
    case failure(ResponseFailure)
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            self = .success(try container.decode(ResponseSuccess.self))
        } catch {
            self = .failure(try container.decode(ResponseFailure.self))
        }
    }
}

Остальные структуры можно оставить как есть, за исключением того, что имена стали camelCase.

struct ResponseFailure : Decodable {
    let message : String
}

struct ResponseSuccess : Decodable {
    let userId : Int
    let token : String
    let tokenType : String
    let expiresIn : Int
}

Чтобы декодировать данные switch в результате, data представляет полученные необработанные данные. Добавлена ​​стратегия декодирования ключей для обработки ключей snake_case.

do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let result = try decoder.decode(Response.self, from: data)
    switch result {
        case .success(let data): print(data)
        case .failure(let error): print(error)
    }
} catch {
    print(error)
}
person vadian    schedule 26.11.2020
comment
Я никогда не видел ничего подобного раньше, но это похоже на хорошее решение. Я скопирую вашу реализацию в следующий раз. - person Alessandro; 26.11.2020

Как упоминалось в комментарии, один из способов - создать один Struct, но тогда вам нужно сделать все свойства Optional, как показано ниже:

struct LoginResponse: Codable {
    let user_id: Int?
    let token: String?
    let token_type : String?
    let expires_in: Int?
    let message: String?
}

Итак, теперь вам нужно разобраться со всеми опциями.

Другой способ, который кажется более подходящим, - ввести еще один Struct, который содержит успех и неудачу, но вы должны реализовать метод init(from decoder: Decoder), как показано ниже:

struct LoginFailure: Codable {
    let message:String
}

struct LoginSuccess: Codable {
    let user_id: Int
    let token: String
    let token_type : String
    let expires_in: Int
}

struct LoginRespone: Codable {
    var data: LoginSuccess?
    var message: LoginFailure?
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let data = try? container.decode(LoginSuccess.self) {
            self.data = data
        } else {
            self.message = try container.decode(LoginFailure.self)
        }
    }
}

let success = """
    {
    "user_id": 41,
    "token": "Token",
    "token_type": "bearer",
    "expires_in": 12096000
    }
""".data(using: .utf8)!

let failure = """
    {
    "message": "this is a failure message"
    }
""".data(using: .utf8)!

    do {
        let r = try JSONDecoder().decode(LoginRespone.self, from: failure)
        print(r.message?.message)
    } catch {
        print(error)
    }
person Kamran    schedule 26.11.2020

Вы можете использовать одну структуру для обработки ответа. На основе кода состояния вы можете различать ответ.

struct LoginModel:Codable {
    let message:String
    let userId: Int
    let token: String
    let tokenType : String
    let expiresIn: Int
    
    enum CodingKeys : String,CodingKey {
        case message, token
        case userId = "user_id"
        case tokenType = "token_type"
        case expiresIn = "expires_in"
    }
    
    init(from decoder: Decoder) throws {
        let value = try decoder.container(keyedBy: CodingKeys.self)
        self.message = try value.decodeIfPresent(String.self, forKey: .message) ?? ""
        self.userId = try value.decodeIfPresent(Int.self, forKey: .userId) ?? 0
        self.token = try value.decodeIfPresent(String.self, forKey: .token) ?? ""
        self.tokenType = try value.decodeIfPresent(String.self, forKey: .tokenType) ?? ""
        self.expiresIn = try value.decodeIfPresent(Int.self, forKey: .expiresIn) ?? 0
    }
}
person agha Shahriyar    schedule 01.12.2020