Handle two different JSON responses from single API endpoint in Swift

I’m working on an authentication system where one API endpoint returns different JSON structures based on whether login succeeds or fails. When authentication works, I get user data like this:

[
  {
    "userId": 456,
    "username": "TestUser",
    "userEmail": "[email protected]",
    "active": "1"
  }
]

When authentication fails, the response looks like:

[
  {
    "errorText": "Authentication failed",
    "active": "0"
  }
]

My current approach uses a single struct but crashes when trying to decode the error response:

struct AuthResponse: Codable {
    let userId: Int
    let username: String
    let userEmail: String
    let active: String
    let errorText: String
    
    enum CodingKeys: String, CodingKey {
        case userId = "userId"
        case username = "username"
        case userEmail = "userEmail"
        case active = "active"
        case errorText = "errorText"
    }
}

func authenticateUser() {
    let userEmail = emailInput.text!
    let userPass = passwordInput.text!
    
    guard let endpoint = URL(string: authAPI) else { return }
    
    URLSession.shared.dataTask(with: endpoint) { (data, response, error) in
        guard let data = data else { return }
        
        do {
            let result = try JSONDecoder().decode([AuthResponse].self, from: data)
            let statusList = result.map { $0.active }
            
            if statusList.contains("1") {
                print("Authentication successful")
                DispatchQueue.main.async {
                    self.navigateToMainScreen()
                }
            } else if statusList.contains("0") {
                DispatchQueue.main.async {
                    self.showErrorAlert()
                }
            }
        } catch {
            print(error)
        }
    }.resume()
}

I get a keyNotFound error because the failure response doesn’t contain userId, username, or userEmail fields. How can I properly handle these two different JSON structures from the same endpoint?

Just make your struct properties optional with ? and check if errorText exists to see if it worked. Way simpler than separate structs. Change let userId: Int to let userId: Int? and use if response.errorText != nil to catch errors.

I hit this exact problem last year. Using an enum with associated values works great here. Create a response enum that handles both cases:

enum AuthResult {
    case success(userId: Int, username: String, userEmail: String)
    case failure(errorText: String)
}

Decode into a basic struct first, then convert to the enum based on what fields are present. This keeps your code clean and makes it obvious what data you’re working with. You can pattern match on the enum result instead of checking the active field - way more readable and easier to maintain.

I hit this exact problem building an e-commerce app - same endpoint, different product availability structures depending on the response. Here’s what worked best for me: create separate structs for each response type and decode conditionally. First, decode into a minimal struct with just the common fields (like your “active” field). Then based on that value, decode again into the proper full struct. No more keyNotFound crashes. I wrapped this in a custom decoder method that returns a Result type. The double decoding barely impacts performance, but you get way cleaner error handling. Much better than scattering optional properties everywhere, especially as your response structures get more complex.