Codificável: lidando com valores de enumeração incorretos

May 09 2023
Protegendo sua base de código: lidando com valores de enum imprevistos Já enfrentou uma situação em que seu aplicativo começou a travar do nada, o que, após investigação, revelou que o back-end forneceu um valor para determinado campo baseado em enum que não existe dentro do enum no código? Considere o seguinte exemplo: Ohhh I love some Tea But what if we change favoriteBeverage in the JSON data from Tea to Juice(um valor que não existe dentro do enum, Beverage) ? Concentre-se na parte da mensagem: “Não é possível inicializar a bebida de um valor de string inválido Juice” Se fosse um aplicativo real em vez de um código de playground, o aplicativo teria travado. Então, como cuidamos do fato de que, mesmo que o back-end envie um valor inválido na resposta, nosso aplicativo deve ser capaz de lidar com isso em vez de travar completamente? Uma das maneiras, e minha favorita:

Protegendo sua base de código: lidando com valores de enumeração imprevistos

Foto de Kostiantyn Li no Unsplash

Já enfrentou uma situação em que seu aplicativo começou a travar do nada, o que, após investigação, revelou que o back-end forneceu um valor para determinado campo baseado em enumeração que não existe dentro da enumeração no código?

Considere o seguinte exemplo:

import Foundation

struct Person: Codable {
    let name: String
    let favouriteBeverage: Beverage
}

enum Beverage: String, Codable {
    case Tea
    case Coffee
}

let jsonString = """
{
    "name": "Shubham Bakshi",
    "favouriteBeverage": "Tea"
}
""".data(using: .utf8)!

let decodedJSON = try! JSONDecoder().decode(Person.self, from: jsonString)

switch decodedJSON.favouriteBeverage {
    
case .Tea:
    print("Ohhh I love some Tea")
    
case .Coffee:
    print("Coffee is no Tea but meh, I'll take it")
}

Ohhh eu amo um pouco de chá

Mas e se mudarmos favouriteBeverageos dados JSON de Tea para Juice (um valor que não existe dentro do enum, Beverage ) ?

let jsonString = """
{
    "name": "Shubham Bakshi",
    "favouriteBeverage": "Juice"
}
""".data(using: .utf8)!

Concentre-se na parte da mensagem: “Não é possível inicializar a bebida de um valor de string inválido Juice”

Se fosse um aplicativo real em vez de um código de playground, o aplicativo teria travado. Então, como cuidamos do fato de que, mesmo que o back-end envie um valor inválido na resposta, nosso aplicativo deve ser capaz de lidar com isso em vez de travar completamente ?

Uma das maneiras, e minha favorita: Mecanismo de fallback de enumeração incorreto.

Diz mecanismo, mas é mais ou menos um protocolo que cuida de tudo. Aqui vamos nos ….

/// This is useful in case the backend specifies some value that does not 
/// exist inside the enum as in such cases the app crashes since it fails to 
/// parse the enum.
/// 
/// This ensures that in case an invalid value is specified, the app will 
/// not crash but rather resort to a value specified for 
/// `fallbackForIncorrectEnumValue`
///
protocol IncorrectEnumFallbackMechanism: RawRepresentable, Codable {
    
    /// Fallback value in case the backend returns an incorrect value for 
    /// the enum
    static var fallbackForIncorrectEnumValue: Self { get }
}

extension IncorrectEnumFallbackMechanism where RawValue: Codable {
    
    init(from decoder: Decoder) throws {
        
        // Fetching the value specified inside the JSON
        let value = try decoder.singleValueContainer().decode(RawValue.self)
        
        // Creating the instance based on the provided value or falling back 
        // to a default value in case the provided value for creating is 
        // invalid
        self = Self(rawValue: value) ?? Self.fallbackForIncorrectEnumValue
    }
    
    /// Since `RawRepresentable` declares a `encode(coder:)` by default so we 
    /// don't need to implement that explicitly
}

enum Beverage: String, IncorrectEnumFallbackMechanism {
    
    static var fallbackForIncorrectEnumValue: Beverage = .SomeWeirdBeverage
    
    case Tea
    case Coffee
    
    case SomeWeirdBeverage
}

Espere! O que é aquilo?

Não importa o que o back-end nos envie como favouriteBeverage, seremos capazes de lidar com isso, ou beber, seja o que for!

Portanto, nosso código completo ficará assim:

import Foundation

/// This is useful in case the backend specifies some value that does not
/// exist inside the enum as in such cases the app crashes since it fails to
/// parse the enum.
///
/// This ensures that in case an invalid value is specified, the app will
/// not crash but rather resort to a value specified for
/// `fallbackForIncorrectEnumValue`
///
protocol IncorrectEnumFallbackMechanism: RawRepresentable, Codable {
    
    /// Fallback value in case the backend returns an incorrect value for
    /// the enum
    static var fallbackForIncorrectEnumValue: Self { get }
}

extension IncorrectEnumFallbackMechanism where RawValue: Codable {
    
    init(from decoder: Decoder) throws {
        
        // Fetching the value specified inside the JSON
        let value = try decoder.singleValueContainer().decode(RawValue.self)
        
        // Creating the instance based on the provided value or falling back
        // to a default value in case the provided value for creating is
        // invalid
        self = Self(rawValue: value) ?? Self.fallbackForIncorrectEnumValue
    }
    
    /// Since `RawRepresentable` declares a `encode(coder:)` by default so we
    /// don't need to implement that explicitly
}

struct Person: Codable {
    let name: String
    let favouriteBeverage: Beverage
}

enum Beverage: String, IncorrectEnumFallbackMechanism {
    
    static var fallbackForIncorrectEnumValue: Beverage = .SomeWeirdBeverage
    
    case Tea
    case Coffee
    
    case SomeWeirdBeverage
}

let jsonString = """
{
    "name": "Shubham Bakshi",
    "favouriteBeverage": "Juice"
}
""".data(using: .utf8)!

let decodedJSON = try! JSONDecoder().decode(Person.self, from: jsonString)

switch decodedJSON.favouriteBeverage {
    
case .Tea:
    print("Ohhh I love some Tea")
    
case .Coffee:
    print("Coffee is no Tea but meh, I'll take it")
    
case .SomeWeirdBeverage:
    print("Wait! What is that?")
}

É isso, pessoal! Codificação feliz!

Você pode se conectar comigo no LinkedIn ou pode entrar em contato comigo por outros canais