I saved some values in UserDefault and I am trying to get it back but I am unable to decode it as it keeps returning fatalError
func save(dataPass: FeaturedProperties) {
var saveArraData: [FeaturedProperties] = getAllData()
saveArraData.append(dataPass)
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(saveArraData) {
defaults.set(encoded, forKey: key)
defaults.synchronize()
print("BOUNCEE CC) saveArraData \(encoded)")
}
}
func getAllData() -> [FeaturedProperties] {
if defaults.value(forKey: key) == nil {
let array = [FeaturedProperties]()
return array
}
if let objects = defaults.data(forKey: key) {
let decoder = JSONDecoder()
do {
let objectsDecoded = try decoder.decode([FeaturedProperties].self, from: objects)
print("BOUNCEE CC) objects \(objectsDecoded)")
return objectsDecoded
} catch let error {
fatalError(error.localizedDescription)
}
} else {
let array = [FeaturedProperties]()
return array
}
}
I am able to print the data but could not decode it to FeaturedProperties any help
error returned is
fatal keyNotFound(FeaturedPropertiesCodingKeys(stringValue: "type_id", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key FeaturedPropertiesCodingKeys(stringValue: \"type_id\", intValue: nil) (\"type_id\").", underlyingError: nil)))
here is my featuredProperties
struct FeaturedProperties: Codable {
var id: Int?
var title: String?
var desc: String?
var typeId: Int?
var subTypeId: Int?
var addressLine1: String?
var addressLine2: String?
var propertyPurposeId: Int?
var isFeatured: Int?
var securityDeposit: Int?
var locationId: Int?
var currency: String?
var price: String?
var pricePerSqm: String?
var leasableArea: String?
var plotSize: String?
var plotPlate: String?
var floorSize: String?
var builtUpArea: String?
var kitchenSize: String?
var yearBuilt: String?
var bathRoomNo: String?
var bedroomNo: String?
var toiletNo: String?
var floorTypeId: String?
var arePetAllowed: String?
var hasKitchen: Int?
var dateAvailable: String?
var status: Int?
var availabilityStatus: Int?
var banner: String?
var premium: Int?
var parkingOptions: String?
var landmarks: String?
var featuresAndAmenities: String?
var createdBy: Int?
var user: User?
var locationDetails: LocationDetails?
var noViews: Int?
var noClicks: Int?
var longitude: String?
var latitude: String?
var isSuspended: Int?
var createdAt: String?
var updatedAt: String?
var deletedAt: String?
var propertyImage: [SearchPropertyImage]?
enum FeaturedPropertiesCodingKeys: String, CodingKey {
case id
case title
case desc = "description"
case typeId = "type_id"
case subTypeId = "sub_type_id"
case addressLine1 = "address_line1"
case addressLine2 = "address_line2"
case propertyPurposeId = "property_purpose_id"
case isFeatured = "is_featured"
case securityDeposit = "security_deposit"
case locationId = "location_id"
case currency
case price
case pricePerSqm = "price_per_sqm"
case leasableArea = "leasable_area"
case plotSize = "plot_size"
case plotPlate = "plot_plate"
case floorSize = "floor_size"
case builtUpArea = "built_up_area"
case kitchenSize = "kitchen_size"
case yearBuilt = "year_built"
case bathRoomNo = "bathroom_no"
case bedroomNo = "bedroom_no"
case toiletNo = "toilet_no"
case floorTypeId = "floor_type_id"
case arePetAllowed = "are_pet_allowed"
case hasKitchen = "has_kitchen"
case dateAvailable = "date_available"
case status
case availabilityStatus = "availability_status"
case banner
case premium
case parkingOptions
case landmarks
case featuresAndAmenities = "features_and_amenities"
case createdBy = "created_by"
case locationDetails = "location_details"
case noViews = "no_views"
case noClicks = "no_clicks"
case longitude
case latitude
case isSuspended = "is_suspended"
case createdAt = "created_at"
case updatedAt = "updated_at"
case deletedAt = "deleted_at"
case propertyImage = "property_image"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: FeaturedPropertiesCodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
title = try container.decodeIfPresent(String.self, forKey: .title)
desc = try container.decodeIfPresent(String.self, forKey: .desc)
typeId = try container.decode(Int.self, forKey: .typeId)
subTypeId = try container.decode(Int.self, forKey: .subTypeId)
addressLine1 = try container.decodeIfPresent(String.self, forKey: .addressLine1)
addressLine2 = try container.decodeIfPresent(String.self, forKey: .addressLine2)
propertyPurposeId = try container.decode(Int.self, forKey: .propertyPurposeId)
isFeatured = try container.decode(Int.self, forKey: .isFeatured)
securityDeposit = try container.decode(Int.self, forKey: .securityDeposit)
locationId = try container.decode(Int.self, forKey: .locationId)
currency = try container.decodeIfPresent(String.self, forKey: .currency)
price = try container.decodeIfPresent(String.self, forKey: .price)
pricePerSqm = try container.decodeIfPresent(String.self, forKey: .pricePerSqm)
leasableArea = try container.decodeIfPresent(String.self, forKey: .leasableArea)
plotSize = try container.decodeIfPresent(String.self, forKey: .plotSize)
plotPlate = try container.decodeIfPresent(String.self, forKey: .plotPlate)
floorSize = try container.decodeIfPresent(String.self, forKey: .floorSize)
builtUpArea = try container.decodeIfPresent(String.self, forKey: .builtUpArea)
kitchenSize = try container.decodeIfPresent(String.self, forKey: .kitchenSize)
yearBuilt = try container.decodeIfPresent(String.self, forKey: .yearBuilt)
bathRoomNo = try container.decodeIfPresent(String.self, forKey: .bathRoomNo)
bedroomNo = try container.decodeIfPresent(String.self, forKey: .bedroomNo)
toiletNo = try container.decodeIfPresent(String.self, forKey: .toiletNo)
floorTypeId = try container.decodeIfPresent(String.self, forKey: .floorTypeId)
arePetAllowed = try container.decodeIfPresent(String.self, forKey: .arePetAllowed)
hasKitchen = try container.decode(Int.self, forKey: .hasKitchen)
dateAvailable = try container.decodeIfPresent(String.self, forKey: .dateAvailable)
status = try container.decode(Int.self, forKey: .status)
availabilityStatus = try container.decode(Int.self, forKey: .availabilityStatus)
banner = try container.decodeIfPresent(String.self, forKey: .banner)
premium = try container.decode(Int.self, forKey: .premium)
parkingOptions = try container.decodeIfPresent(String.self, forKey: .parkingOptions)
landmarks = try container.decodeIfPresent(String.self, forKey: .landmarks)
featuresAndAmenities = try container.decodeIfPresent(String.self, forKey: .featuresAndAmenities)
createdBy = try container.decode(Int.self, forKey: .createdBy)
noViews = try container.decode(Int.self, forKey: .noViews)
noClicks = try container.decode(Int.self, forKey: .noClicks)
longitude = try container.decodeIfPresent(String.self, forKey: .longitude)
latitude = try container.decodeIfPresent(String.self, forKey: .latitude)
isSuspended = try container.decode(Int.self, forKey: .isSuspended)
createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt)
locationDetails = try container.decodeIfPresent(LocationDetails.self, forKey: .locationDetails)
updatedAt = try container.decodeIfPresent(String.self, forKey: .updatedAt)
deletedAt = try container.decodeIfPresent(String.self, forKey: .deletedAt)
propertyImage = try container.decodeIfPresent([SearchPropertyImage].self, forKey: .propertyImage)
}
}
You have obvious mistakes in your decoding process. For example typeId
is optional but you are using decode
instead of decodeIfPresent
. To prevent mistakes, let the compiler generate everything for you:
struct FeaturedProperties: Codable {
var id: Int?
var title: String?
var desc: String?
var typeId: Int?
var subTypeId: Int?
var addressLine1: String?
var addressLine2: String?
var propertyPurposeId: Int?
var isFeatured: Int?
var securityDeposit: Int?
var locationId: Int?
var currency: String?
var price: String?
var pricePerSqm: String?
var leasableArea: String?
var plotSize: String?
var plotPlate: String?
var floorSize: String?
var builtUpArea: String?
var kitchenSize: String?
var yearBuilt: String?
var bathRoomNo: String?
var bedroomNo: String?
var toiletNo: String?
var floorTypeId: String?
var arePetAllowed: String?
var hasKitchen: Int?
var dateAvailable: String?
var status: Int?
var availabilityStatus: Int?
var banner: String?
var premium: Int?
var parkingOptions: String?
var landmarks: String?
var featuresAndAmenities: String?
var createdBy: Int?
var user: User?
var locationDetails: LocationDetails?
var noViews: Int?
var noClicks: Int?
var longitude: String?
var latitude: String?
var isSuspended: Int?
var createdAt: String?
var updatedAt: String?
var deletedAt: String?
var propertyImage: [SearchPropertyImage]?
}
Would be enough.
Of course, your could add CodingKeys
for optional override but if your only aim is to translate camel case identifiers to underscores, you can use key decoding strategy on the JSON decoder/encoder, e.g.
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
If you want to use custom coding keys, the enum must be called CodingKeys
, e.g.
private enum CodingKeys: String, CodingKey {
case typeId = "type_id"
}