Home > Mobile >  how to resolve the typeMismatch error in SwiftUI?
how to resolve the typeMismatch error in SwiftUI?

Time:09-23

This is my model. When I fetch the detail the an error occured.

import Foundation

// MARK: - PublisherModel
public struct PublisherModel: Decodable {
    public let userslist: [UserslistModel]

    public init(userslist: [UserslistModel]) {
        self.userslist = userslist
    }
}

// MARK: - UserslistModel
public struct UserslistModel: Decodable, Hashable {
    public var id: String
    public let full_name: String
    public let totalBookViews: String
    public let totalBooks: String?
    public let full_address: String?
    private enum CodingKeys: String, CodingKey {
            case id,full_name,totalBookViews,totalBooks,full_address
        }
    public init(id: String, full_name: String, totalBookViews: String?, totalBooks: String?, full_address: String?) {
        self.id = id
        self.full_name = full_name
        self.totalBookViews = totalBookViews!
        self.totalBooks = totalBooks!
        self.full_address = full_address!
    }
    
   public init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
       full_name = try container.decode(String.self, forKey: .full_name)
            full_address = try container.decode(String.self, forKey: .full_address)
       totalBooks = try container.decode(String.self, forKey: .totalBooks)
       totalBookViews = try container.decode(String.self, forKey: .totalBookViews)
            do {
                id = try String(container.decode(Int.self, forKey: .id))
            } catch DecodingError.typeMismatch {
                id = try container.decode(String.self, forKey: .id)
                
            }
        }
}

This is My ViewModel

class PublisherModelVM: ObservableObject {
    @Published var datas = [UserslistModel]()
   
    let url = "https://alibrary.in/api/publisherList"
    
    init() {
        getData(url: url)
    }
    
    func getData(url: String) {
        guard let url = URL(string: url) else { return }
        URLSession.shared.dataTask(with: url) { (data, _, _) in
            if let data = data {
                do {
                    let results = try JSONDecoder().decode(PublisherModel.self, from: data).userslist
                    DispatchQueue.main.async {
                        self.datas = results.reversed()
                    }
                }
                catch {
                    print(error)
                }
            }
        }.resume()
    }
    
}

This is My View here I fetch the details

struct PublisherDataView: View{
    @StateObject var list = PublisherModelVM()
    @State var logourl: String?
    var body: some View{
            ScrollView(.horizontal,showsIndicators: false){
                HStack{
                    ForEach( list.datas, id: \.id){ item in

                        
                        VStack(spacing: 12){
                              Text(item.full_name )
                        Text(item.full_address ?? "-")
                        Text(item.totalBookViews)
                        Text(item.totalBooks!)
                        }
                        .frame(width:275, height: 220)
                        .background(Color.brown.opacity(0.5)).cornerRadius(12)
                        
                    
                }
            }
        }
    }
}

And when run this code the a error show that is:

typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "userslist", intValue: nil), _JSONKey(stringValue: "Index 2", intValue: 2), CodingKeys(stringValue: "totalBookViews", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))

Where am I doing a mistake?

And whenever I changed the totalBookViews with Int then this new error show:

typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "userslist", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "id", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))

Where am I wrong? Is it whenever I changed full_address with String? Then also this issue appeared:

typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "userslist", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "totalBooks", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))

CodePudding user response:

First of all please delete all init methods in your structs. The compiler creates them on your behalf, and Codable doesn't use them anyway.

public init(userslist: [UserslistModel]) {
    self.userslist = userslist
}
and
    public init(id: Int, full_name: String, totalBookViews: String, totalBooks: Int, full_address: String) {
    self.id = id
    self.full_name = full_name
    self.totalBookViews = totalBookViews
    self.totalBooks = totalBooks
    self.full_address = full_address
}

Second of all add the convertFromSnakeCase strategy to map the snake_case keys to camelCase struct members.

totalBookViews is sometimes Int and sometimes String. It's still more complicating: The string value cannot be directly converted to Int because it contains letters and whitespace characters.

You have to implement init(from decoder and decode totalBookViews conditionally. If it's a string, filter the numeric characters and convert the string to Int.

Another issue – already mentioned by Nirav D – is that fullAddress can be nil. The init method must consider that, too.

// MARK: - PublisherModel
public struct PublisherModel: Decodable {
    public let userslist: [UserslistModel]
}

// MARK: - UserslistModel
public struct UserslistModel: Decodable {
    public let id: Int
    public let fullName: String
    public let totalBookViews: Int
    public let totalBooks: Int
    public let fullAddress: String
    
    private enum CodingKeys: String, CodingKey {
        case id, fullName, totalBookViews, totalBooks, fullAddress
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(Int.self, forKey: .id)
        self.fullName = try container.decode(String.self, forKey: .fullName)
        do {
            self.totalBookViews = try container.decode(Int.self, forKey: .totalBookViews)
        } catch {
            let bookViews = try container.decode(String.self, forKey: .totalBookViews).filter(\.isNumber)
            guard let bookViewsAmount = Int(bookViews) else {
                throw DecodingError.dataCorruptedError(forKey: .totalBookViews, in: container, debugDescription: "Wrong String Format")
            }
            self.totalBookViews = bookViewsAmount
        }
        
        self.totalBooks = try container.decode(Int.self, forKey: .totalBooks)
        self.fullAddress = try container.decodeIfPresent(String.self, forKey: .fullAddress) ?? ""
    }
}   

And create the decoder

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let results = try decoder.decode(PublisherModel.self, from: data).userslist
  • Related