Solucionado (ver solução)
Solucionado
(ver solução)
3
respostas

[Dúvida] Erro ao cadastrar usuário

Quando eu clico no botão de cadastrar o paciente, o catch retorna um erro:

Houve um erro ao cadastrar paciente keyNotFound(CodingKeys(stringValue: "nome", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"nome\", intValue: nil) (\"nome\").", underlyingError: nil))

Já verifiquei se minhas CodingKeys estão de acordo com o retorno da API, mas não sei mais o que pode estar causando esse erro

struct Paciente

//
//  Patient.swift
//  Vollmed
//
//  Created by Juan Carlos Parizotto da Silva  on 24/11/25.
//

import Foundation

struct Patient: Identifiable, Codable {
    let id: String?
    let name: String
    let email: String
    let password: String
    let cpf: String
    let phoneNumber: String
    let healthPlan: String
    
    enum CodingKeys : String, CodingKey {
        case id
        case name = "nome"
        case email
        case password = "senha"
        case cpf
        case phoneNumber = "telefone"
        case healthPlan = "planoSaude"
    }
}

WebService

struct WebService {
    
    private let baseURL = "http://localhost:3000"
    private let imageCache = NSCache<NSString, UIImage>()
    
    func registerPatient(_ patient: Patient) async throws -> Patient? {
        let endpoint = baseURL + "/paciente"
        
        guard let url = URL(string: endpoint) else {
            print("Erro ao buscar URL")
            return nil
        }
        
        let jsonData = try JSONEncoder().encode(patient)

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = jsonData
        
        let (data, _) = try await URLSession.shared.data(for: request)
        
        let patient = try JSONDecoder().decode(Patient.self, from: data)
        
        return patient
    }
    [...]
3 respostas

Olá Juan.
Vou dar algumas sugestões que podem ajudar.
Confere ai:
O erro diz:

keyNotFound(CodingKeys(stringValue: "nome", intValue: nil)
"No value associated with key 'nome'"

Isso significa que a resposta da sua API NO POST não contém o campo nome.
Mas seu modelo Patient OBRIGA esse campo (let name: String).
Então na hora de decodificar o retorno da API, o Swift tenta achar "nome" (porque seu CodingKeys mapeia name = "nome") ,mas a chave não existe na resposta JSON, e aí o decode falha.
Antes do decode, coloque:

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

Isso vai mostrar a resposta exata que sua API está retornando.
MUITO provavelmente vai ser algo assim:

{
  "id": "123"
}

Ou:

{
  "message": "Paciente criado com sucesso"
}

Ou outro JSON sem o campo nome.

Como corrigir

  • Ajustar o modelo de retorno (recomendado)
    A resposta do backend após um POST normalmente não retorna o mesmo objeto completo.
    Então crie um modelo APENAS para a resposta:
struct PatientResponse: Codable {
    let id: String?
    let message: String?
}

E no registerPatient decodifique assim:

let response = try JSONDecoder().decode(PatientResponse.self, from: data)
return nil  // ou retorne response, caso precise
  • Deixar campos opcionais
    Se quiser continuar usando Patient, torne as propriedades opcionais para permitir decodificação parcial:
struct Patient: Identifiable, Codable {
    let id: String?
    let name: String?
    let email: String?
    let password: String?
    let cpf: String?
    let phoneNumber: String?
    let healthPlan: String?
}

Mas isso não é ideal, pois agora seu modelo fica “fraco”.

  • Não tentar decodificar nada (se não precisar)
    Se você só quer saber se deu certo:
_ = try await URLSession.shared.data(for: request)
return nil

Ou:

let (_, response) = try await URLSession.shared.data(for: request)

if let httpResponse = response as? HTTPURLResponse {
    print("Status: \(httpResponse.statusCode)")
}

Você está enviando corretamente o JSON no POST.
O PROBLEMA está no JSON que a API devolve, não no que você envia.
O decode falha porque o retorno NÃO possui a chave "nome".
Teste ai e avise o resultado.
Bons estudos.

Opa Ronaldo, obrigado por responder. Ainda não testei suas recomendações, mas vou fazer
Tu disse que a API não retorna a chave "nome", mas isso que me causa mais dúvida, porque quando eu testo registrar um novo paciente pelo Postman a API devolve o JSON:

{
    "cpf": "11111111111",
    "nome": "Juan 2",        // <----- o campo "nome"
    "email": "juan2@email.com",
    "senha": "9e9c2945e5bf948581961943a0c4a1fe:c6d915b2e4b481d6b60348a3f173c9c5",
    "telefone": 99999999,
    "planoSaude": "Unimed",
    "id": "ff09e37f-f784-4afa-8b31-c7d6014bdc12"
}

Por isso acho estranho, porque a API retorna sim o campo "nome". porém continua retornando como se estivesse errado. Você teria alguma recomendação sabendo disso agora?

solução!

E ai Juan.
Mesmo que o JSON que você recebe no Postman tenha o campo "nome", é possível que o seu modelo Patient use uma chave diferente na propriedade correspondente, por exemplo, name.
Ou seja, a chave "nome" do JSON está sendo mapeada para a propriedade name do seu modelo, mas isso não está acontecendo corretamente durante a decodificação.
No Swift, quando você usa o Codable e não define explicitamente como mapear as chaves do JSON para as propriedades do modelo, o Swift tenta fazer esse mapeamento automaticamente. Se o nome da chave no JSON for diferente do nome da variável na estrutura do modelo, a decodificação falha.
Você pode especificar explicitamente como as chaves do JSON são mapeadas para as propriedades do seu modelo Patient.
Para isso, crie um enum CodingKeys dentro da sua struct que mapeia cada chave do JSON para a propriedade correspondente. Como a chave no JSON é "nome", mas a propriedade no modelo é name, você deve mapear isso.
Exemplo :

struct Patient: Identifiable, Codable {
    let id: String?
    let name: String?
    let email: String?
    let password: String?
    let cpf: String?
    let phoneNumber: String?
    let healthPlan: String?
    
    enum CodingKeys: String, CodingKey {
        case id
        case name = "nome"  // Aqui você está dizendo que "name" é mapeado para "nome" no JSON
        case email
        case password = "senha"
        case cpf
        case phoneNumber = "telefone"
        case healthPlan = "planoSaude"
    }
}

Dessa forma, você vai mapear as chaves corretas do JSON para as variáveis no seu modelo.
Como mencionou que a API está devolvendo a chave "nome" no Postman, vamos garantir que a resposta da API que você está recebendo no código é a mesma.
Você pode colocar o código para imprimir a resposta bruta da API antes de tentar decodificar.
Isso vai ajudar a garantir que o JSON retornado realmente está como esperado:

let data = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8)!)  // Isso vai te mostrar o JSON como string

let response = try JSONDecoder().decode(Patient.self, from: data)

Dessa forma, você vai ver o JSON completo e confirmar se o campo "nome" está sendo retornado corretamente.
Se a API realmente retornar respostas parciais ou em formatos diferentes, uma boa prática é usar propriedades opcionais no seu modelo para evitar falhas de decodificação quando alguma chave estiver faltando:

struct Patient: Identifiable, Codable {
    let id: String?
    let name: String?
    let email: String?
    let password: String?
    let cpf: String?
    let phoneNumber: String?
    let healthPlan: String?
}

Com isso, você evita erros de decodificação quando algum campo esperado não estiver presente na resposta.
Teste essas mudanças e veja se o problema persiste.
Se ainda tiver algum erro ou se a situação for diferente, me avise.
Boa sorte com seu projeto.
Bons estudos.