Type 'User' does not conform to protocol 'Decodable'

hi, so I have 3 files in my xcode:

ContentView

import SwiftUI

struct ContentView: View {
    @State private var email = ""
    @State private var password = ""
    @State private var wrongEmail = 0
    @State private var wrongPassword = 0
    @State private var showingLoginScreen = false
    @State private var userData: User? // Declare a @State property for UserData

    
    var body: some View {
        NavigationView {
            ZStack {
                Color.blue
                    .ignoresSafeArea()
                Circle()
                    .scale(1.7)
                    .foregroundColor(.white.opacity(0.15))
                Circle()
                    .scale(1.35)
                    .foregroundColor(.white)
                VStack {
                    Text("Login")
                        .font(.largeTitle)
                        .bold().padding()
                    TextField("Email", text: $email)
                        .padding()
                        .frame(width: 300, height: 50)
                        .background(Color.black.opacity(0.05))
                        .cornerRadius(10)
                        .border(.red, width: CGFloat(wrongEmail))
                    SecureField("Password", text: $password)
                        .padding()
                        .frame(width: 300, height: 50)
                        .background(Color.black.opacity(0.05))
                        .cornerRadius(10)
                        .border(.red, width: CGFloat(wrongPassword))
                    Button("Login") {
                        authenticateUser(email: email, password: password)
                    }
                    .foregroundColor(.white)
                    .frame(width: 300, height: 50)
                    .background(Color.blue)
                    .cornerRadius(10)
                    
                    NavigationLink(
                                destination: ArtworkView(user: userData),
                                isActive: $showingLoginScreen
                            ) {
                                EmptyView()
                            }
//                    NavigationLink(destination: Text("You are logged in @\(email)"), isActive: $showingLoginScreen) {
//                        EmptyView()
//                    }
                }
            }.navigationBarHidden(true)
        }
    }
    func authenticateUser(email: String, password: String) {
        guard  let url = URL(string: "https://api.hangn.art/api/login") else {
            return
        }
        
        print("making api call")
        
        var request = URLRequest(url: url)
        // method, body, headers
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        let body: [String: AnyHashable] = [
            "email": email,
            "password": password
        ]
        request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
        //Make the request
        let task = URLSession.shared.dataTask(with: request) { data, _, error in
            guard let data = data, error == nil else {
                return
            }
            print(String(data: data, encoding: .utf8))
            let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase // Configure snake case decoding
            do {
//                let response = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
                let response = try decoder.decode(LoginResponse.self, from: data)
                            self.userData = response.user
                let userData = try decoder.decode(User.self, from: data)
                print("SUCCESS: \(userData)")
                wrongEmail = 0
                wrongPassword = 0
                showingLoginScreen = true
            }
            catch {
                print("Error parsing response data: \(error)")
                    print("Response data: \(String(data: data, encoding: .utf8) ?? "")")
            }
        }
        task.resume()
    }
}

//struct Response: Codable {
//    let email: String
//    let password: String
//}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

ArtworkView

import SwiftUI

struct ArtworkView: View {
    var user: User?

        var body: some View {
            if let user = user {
                VStack {
                    Text("Name: \(user.firstName) \(user.lastName)")
                    Text("Email: \(user.email)")
                    Text("Description: \(user.description)")
                    // ... display other properties ...
                }
            } else {
                Text("Loading...")
            }
        }
}

User

import Foundation

struct LoginResponse: Decodable {
    let token: String
    let user: User
}

struct User: Decodable {
    let clientId: Int
    let createdAt: String
    let description: String
    let email: String
    let emailVerifiedAt: String?
    let firstName: String
    let id: Int
    let image: String
    let lastName: String
    let location: String? // Make it optional
    let pmLastFour: String? // Make it optional
    let pmType: String? // Make it optional
    let stripeId: String
    let trialEndsAt: String? // Make it optional
    let updatedAt: String

    // Define custom keys for snake case properties
    private enum CodingKeys: String, CodingKey {
        case clientId = "client_id"
        case createdAt = "created_at"
        case emailVerifiedAt = "email_verified_at"
        case firstName = "first_name"
        case lastName = "last_name"
        case pmLastFour = "pm_last_four"
        case pmType = "pm_type"
        case stripeId = "stripe_id"
        case trialEndsAt = "trial_ends_at"
        // ... other properties ...
    }
}

why am I getting: Type 'User' does not conform to protocol 'Decodable'

Answered by Scott in 763315022

Or try just removing this line:

decoder.keyDecodingStrategy = .convertFromSnakeCase // Configure snake case decoding

You are already supplying an explicit enum CodingKeys that defines the key conversion, so asking for additional conversion produces unexpected results.

That's just because your codingKeys are not complete, there are missing cases.

No more error once completed:

    private enum CodingKeys: String, CodingKey {
        case clientId = "client_id"
        case createdAt = "created_at"
        case description = "description"
        case email = "email"
        case emailVerifiedAt = "email_verified_at"
        case firstName = "first_name"
        case id = "id"
        case image = "image"
        case lastName = "last_name"
        case location = "location"
        case pmLastFour = "pm_last_four"
        case pmType = "pm_type"
        case stripeId = "stripe_id"
        case trialEndsAt = "trial_ends_at"
        case updatedAt = "updatedAt"
        // ... other properties ...
    }

YAY thanks, no errors now apart from when I log in.

Error parsing response data: keyNotFound(CodingKeys(stringValue: "client_id", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "user", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: "client_id", intValue: nil) ("client_id").", underlyingError: nil))

What is the data you try decoding ?

Please show the result of the print

            print(String(data: data, encoding: .utf8))

You probably have no client_id value.

Response data: {"user":{"id":2,"client_id":1,"first_name":"John","last_name":"Biddulph","email":"John.***[at]gmail.com","email_verified_at":null,"image":"1690027187.jpeg","location":null,"description":"Artist","created_at":"2023-07-22T10:51:57.000000Z","updated_at":"2023-07-22T11:59:47.000000Z","stripe_id":"cus_OJFvSSUgRbDTIi","pm_type":null,"pm_last_four":null,"trial_ends_at":null},"token":"46|ZZZjWGOQusPzsv5AA8yCmUpndoDikZha7F0lspe2"}

underscore (client_id) cause problems : https://stackoverflow.com/questions/55816336/swift-jsondecoder-coding-keys-not-working-with-underscore

So, when you get the json, you have to remove them from keys before decoding.

Accepted Answer

Or try just removing this line:

decoder.keyDecodingStrategy = .convertFromSnakeCase // Configure snake case decoding

You are already supplying an explicit enum CodingKeys that defines the key conversion, so asking for additional conversion produces unexpected results.

I'm still getting:

Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "user", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: "updatedAt", intValue: nil) ("updatedAt").", underlyingError: nil))

I'm still getting:

still getting with what code precisely ? Removing snakeCase ? Removingundesrcore (both in codingKeys AND in the JSON you receive) ?

But you should first close the thread on the initial question's answer and create a new one, to avoid never ending threads.

Type 'User' does not conform to protocol 'Decodable'
 
 
Q