Có thể mã hóa: Xử lý các giá trị Enum không chính xác

May 09 2023
Bảo vệ cơ sở mã của bạn: xử lý các giá trị enum không lường trước Bạn đã bao giờ gặp phải tình huống trong đó ứng dụng của bạn bắt đầu gặp sự cố đột ngột, khi điều tra cho thấy rằng chương trình phụ trợ đã cung cấp một giá trị cho trường dựa trên enum nhất định không tồn tại bên trong mã enum? Xem xét ví dụ sau: Ohhh Tôi yêu một ít Trà Nhưng điều gì sẽ xảy ra nếu chúng ta thay đổi favouriteBeverage trong dữ liệu JSON từ Tea thành Juice (một giá trị không tồn tại bên trong enum, Beverage)? Tập trung vào phần của thông báo: “Không thể khởi tạo Đồ uống từ Nước trái cây có giá trị Chuỗi không hợp lệ” Nếu đó là một ứng dụng thực thay vì mã sân chơi, thì ứng dụng sẽ bị lỗi. Vì vậy, làm cách nào để chúng tôi xử lý thực tế là ngay cả khi chương trình phụ trợ gửi một giá trị không hợp lệ trong phản hồi, ứng dụng của chúng tôi sẽ có thể xử lý nó thay vì bị sập hoàn toàn? Một trong những cách, và yêu thích cá nhân của tôi:

Bảo vệ cơ sở mã của bạn: xử lý các giá trị enum không lường trước được

Ảnh của Kostiantyn Li trên Bapt

Bạn đã bao giờ gặp phải tình huống trong đó ứng dụng của bạn bắt đầu gặp sự cố đột ngột, khi điều tra cho thấy rằng chương trình phụ trợ đã cung cấp một giá trị cho trường dựa trên enum nhất định không tồn tại bên trong enum trong mã?

Hãy xem xét ví dụ sau:

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")
}

Ohhhh tôi thích một ít trà

Nhưng nếu chúng ta thay đổi favouriteBeveragedữ liệu JSON từ Tea thành Juice (một giá trị không tồn tại bên trong enum, Beverage ) thì sao?

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

Tập trung vào một phần của thông báo: “Không thể khởi tạo Đồ uống từ giá trị Chuỗi không hợp lệ Nước ép”

Nếu đó là một ứng dụng thực tế thay vì mã sân chơi, thì ứng dụng đó sẽ bị lỗi. Vì vậy, làm cách nào để chúng tôi xử lý thực tế là ngay cả khi chương trình phụ trợ gửi một giá trị không hợp lệ trong phản hồi, ứng dụng của chúng tôi sẽ có thể xử lý nó thay vì bị lỗi hoàn toàn ?

Một trong những cách và sở thích cá nhân của tôi: Cơ chế dự phòng Enum không chính xác.

Nó nói cơ chế nhưng ít nhiều là một giao thức đảm nhận mọi thứ. Vì vậy, ở đây chúng tôi đi….

/// 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
}

Chờ đợi! Đó là gì?

Bất kể phần phụ trợ gửi cho chúng tôi là gì favouriteBeverage, chúng tôi sẽ có thể xử lý hoặc uống nó, bất cứ điều gì!

Vì vậy, mã hoàn chỉnh của chúng tôi bây giờ sẽ trông như thế này:

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?")
}

Vậy đó, các bạn! Mã hóa vui vẻ!

Bạn có thể kết nối với tôi trên LinkedIn hoặc có thể liên hệ với tôi qua các kênh khác