You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让协议模型支持String/Object等多种已知类型的属性?

Handling Mixed Response Types in Your Login API Protocol

Got it, let's tackle this problem head-on. The core issue here is that your login API returns two different formats for the error_message field in the response object—sometimes a plain string, other times a dictionary of field-specific errors. Your current protocol is tied to a single UserLoginEntity type, which can't handle this variation. Here are two solid approaches to fix this:

Approach 1: Use an Enum to Wrap Error Variants

This is the most straightforward solution, as it lets you keep your original protocol structure while making UserLoginEntity flexible enough to handle both error types.

First, define an enum that covers both error scenarios:

enum LoginErrorDetail {
    case singleMessage(String)
    case fieldSpecificErrors([String: [String]])
}

Next, update your UserLoginEntity to use this enum instead of a fixed type for the error message:

struct UserLoginEntity {
    let errorMessage: LoginErrorDetail
    // Add any other fields from the response here if needed
}

Then, implement custom Codable conformance to handle the dual decoding logic (this works if you're using Swift's built-in Codable or libraries like Alamofire that leverage it):

extension UserLoginEntity: Codable {
    enum CodingKeys: String, CodingKey {
        case errorMessage = "error_message"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // Try decoding as a single string first
        if let singleError = try? container.decode(String.self, forKey: .errorMessage) {
            errorMessage = .singleMessage(singleError)
        } 
        // If that fails, try decoding as a field error dictionary
        else if let fieldErrors = try? container.decode([String: [String]].self, forKey: .errorMessage) {
            errorMessage = .fieldSpecificErrors(fieldErrors)
        } 
        // If neither works, throw a decoding error
        else {
            throw DecodingError.dataCorruptedError(
                forKey: .errorMessage,
                in: container,
                debugDescription: "Unsupported error message format received"
            )
        }
    }
    
    // Optional: Implement encode if you need to send these errors back
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        switch errorMessage {
        case .singleMessage(let message):
            try container.encode(message, forKey: .errorMessage)
        case .fieldSpecificErrors(let errors):
            try container.encode(errors, forKey: .errorMessage)
        }
    }
}

Now your original UserLoginResponse protocol works perfectly—response is still a UserLoginEntity?, but the entity can now represent both error scenarios. When using the response, you can switch on the enum to handle each case:

if let response = loginResponse.response {
    switch response.errorMessage {
    case .singleMessage(let message):
        print("General error: \(message)")
    case .fieldSpecificErrors(let errors):
        print("Field errors: \(errors)")
    }
}

Approach 2: Use Associated Types for Protocol Flexibility

If you need more granular control (e.g., handling the two response types as distinct models), you can modify your protocol to use an associated type:

protocol UserLoginResponse {
    var id: Int { get }
    var success: Bool { get }
    var code: Int { get }
    associatedtype ResponseContent
    var response: ResponseContent? { get }
}

Then create separate structs for each response type:

// For the single error message scenario
struct SingleErrorLoginResponse: UserLoginResponse {
    let id: Int
    let success: Bool
    let code: Int
    var response: SingleErrorEntity?
}

struct SingleErrorEntity: Codable {
    let errorMessage: String
    enum CodingKeys: String, CodingKey {
        case errorMessage = "error_message"
    }
}

// For the field-specific error scenario
struct FieldErrorLoginResponse: UserLoginResponse {
    let id: Int
    let success: Bool
    let code: Int
    var response: FieldErrorEntity?
}

struct FieldErrorEntity: Codable {
    let errorMessage: [String: [String]]
    enum CodingKeys: String, CodingKey {
        case errorMessage = "error_message"
    }
}

When parsing the API response, you'll need to check which format you're receiving (e.g., by checking if the error_message field is a string or object) and initialize the appropriate response struct.

This approach is useful if you want to treat the two response types as separate entities in your codebase, but it adds more type complexity compared to the enum approach.


内容的提问来源于stack exchange,提问作者Drey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:18:46