Search code examples
swiftcore-dataunrecognized-selectorcodable

Codable CoreData models crash with `Unrecognized selector sent to instance`


So, I'm in the midst of trying to conform my model classes to NSManagedObject (for CoreData persistence) and Codable (For JSON serialization/deserialization). Right now my JSON decoder gets about halfway through deserialization before it crashes with the error

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Land setGun:]: unrecognized selector sent to instance 0x60400028f780'

I've been following the guide for conformance to both model protocols found here How to use swift 4 Codable in Core Data? and I realize that the error is thrown because (if the error is to be believed) the Land class doesn't have a 'setGun' method. What boggles me is

A) The error doesn't occur while I'm decoding Land class JSON (it handles that without a problem), the last breakpoint to be hit is actually in Sea class's JSON decoder.

B) The Land class DOES have a 'setGun' method (I declared a placeholder method in the class just to be extra sure in case it was something to do with the auto-generated CoreData code).

My code for the Land class is below:

import Foundation
import CoreData
@objc(Land)
class Land : NSManagedObject, Equipment {
var type = EquipmentType.LAND

enum CodingKeys: String, CodingKey {
    case id
    case name
    case desc = "description"
    case groupIconUrl
    case individualIconUrl
    case photoUrl
    case primaryWeapon
    case secondaryWeapon
    case atgm
    case armor
    case speed
    case auto
    case weight
}

required convenience init(from decoder: Decoder) throws {
    guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else { fatalError() }
    guard let entity = NSEntityDescription.entity(forEntityName: "Land", in: context) else { fatalError() }
    self.init(entity: entity, insertInto: context)
    let container = try! decoder.container(keyedBy: CodingKeys.self)
    self.id = try container.decode(Int64.self, forKey: .id)
    self.name = try container.decodeIfPresent(String.self, forKey: .name)
    self.desc = try container.decodeIfPresent(String.self, forKey: .desc)
    self.groupIconUrl = try container.decodeIfPresent(String.self, forKey: .groupIconUrl)
    self.individualIconUrl = try container.decodeIfPresent(String.self, forKey: .individualIconUrl)
    self.photoUrl = try container.decodeIfPresent(String.self, forKey: .photoUrl)
    self.primaryWeapon = try container.decodeIfPresent(Gun.self, forKey: .primaryWeapon)
    self.secondaryWeapon = try container.decodeIfPresent(Gun.self, forKey: .secondaryWeapon)
    self.atgm = try container.decodeIfPresent(Gun.self, forKey: .atgm)
    self.armor = try container.decode(Int64.self, forKey: .armor)
    self.speed = try container.decode(Int64.self, forKey: .speed)
    self.auto = try container.decode(Int64.self, forKey: .auto)
    self.weight = try container.decode(Int64.self, forKey: .weight)
}
public func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(id, forKey: .id)
    try container.encode(name, forKey: .name)
    try container.encode(desc, forKey: .desc)
    try container.encode(groupIconUrl, forKey: .groupIconUrl)
    try container.encode(individualIconUrl, forKey: .individualIconUrl)
    try container.encode(photoUrl, forKey: .photoUrl)
    try container.encode(primaryWeapon, forKey: .primaryWeapon)
    try container.encode(secondaryWeapon, forKey: .secondaryWeapon)
    try container.encode(atgm, forKey: .atgm)
    try container.encode(armor, forKey: .armor)
    try container.encode(speed, forKey: .speed)
    try container.encode(auto, forKey: .auto)
    try container.encode(weight, forKey: .weight)
}
public func setGun(gun: Gun){}
}

My CoreData dataModel is also screen-capped below:

CoreData dataModel

Finally, if all that isn't enough to diagnose what's going wrong, you're free to clone the repository and run it locally from the following public github repository: https://github.com/jamesjmtaylor/weg-ios


Solution

  • Your issue in this lines at Air.swift file I'm really don't know why don't decode Gun at Air file Decoder

    self.gun = try container.decodeIfPresent(Gun.self, forKey: .gun)
    self.agm = try container.decodeIfPresent(Gun.self, forKey: .agm)
    self.aam = try container.decodeIfPresent(Gun.self, forKey: .aam)
    self.asm = try container.decodeIfPresent(Gun.self, forKey: .asm)