How do I properly use Codable, CoreData and NSSet relationships?

被刻印的时光 ゝ 提交于 2020-06-09 04:47:21

问题


I'm following this tutorial to implement CoreData with Codable. Everything seems to be going great however I cannot figure out how to encode my list of photo objects. You can see my data structure in the image below and view my current code. When I try to decode the photos objects in the Pin class as below I get the error:

Referencing instance method 'encode(_:forKey:)' on 'Optional' requires that 'NSSet' conform to 'Encodable'

Photo+CoreDataClass.swift

import Foundation
import CoreData

@objc(Photo)
public class Photo: NSManagedObject, Codable {

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        do {
            try container.encode(id, forKey: .id)
            try container.encode(owner, forKey: .owner)
            try container.encode(server, forKey: .server)
            try container.encode(secret, forKey: .secret)
            try container.encode(title, forKey: .title)
            try container.encode(isPublic, forKey: .isPublic)
            try container.encode(isFriend, forKey: .isFriend)
            try container.encode(isFamily, forKey: .isFamily)
        }
    }

    required convenience public init(from decoder: Decoder) throws {
        guard let contextUserInfoKey = CodingUserInfoKey(rawValue: "context"),
            let managedObjectContext = decoder.userInfo[contextUserInfoKey] as? NSManagedObjectContext,
            let entity = NSEntityDescription.entity(forEntityName: "Photo", in: managedObjectContext) else {
                fatalError("Cannot decode Photo!")
        }
        self.init(entity: entity, insertInto: managedObjectContext)
        let values = try decoder.container(keyedBy: CodingKeys.self)
        do {
            id = try values.decode(Int64.self, forKey: .id)
            owner = try values.decode(String?.self, forKey: .owner)
            server = try values.decode(String?.self, forKey: .server)
            secret = try values.decode(String?.self, forKey: .secret)
            title = try values.decode(String?.self, forKey: .title)
            isPublic = try values.decode(Int16.self, forKey: .isPublic)
            isFriend = try values.decode(Int16.self, forKey: .isFriend)
            isFamily = try values.decode(Int16.self, forKey: .isFamily)
        } catch {
            print(error)
        }
    }

    enum CodingKeys: String, CodingKey {
        case id = "id"
        case owner = "owner"
        case server = "server"
        case secret = "secret"
        case title = "title"
        case isPublic = "ispublic"
        case isFriend = "isfriend"
        case isFamily = "isfamily"
    }

}

Photo+CoreDataProperties.swift

import Foundation
import CoreData


extension Photo {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Photo> {
        return NSFetchRequest<Photo>(entityName: "Photo")
    }

    @NSManaged public var id: Int64
    @NSManaged public var owner: String?
    @NSManaged public var secret: String?
    @NSManaged public var server: String?
    @NSManaged public var title: String?
    @NSManaged public var isPublic: Int16
    @NSManaged public var isFriend: Int16
    @NSManaged public var isFamily: Int16

}

Pin+CoreDataClass.swift

import Foundation
import CoreData

@objc(Pin)
public class Pin: NSManagedObject, Codable {

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(latitude, forKey: .latitude)
        try container.encode(longitude, forKey: .longitude)
        try container.encode(photos, forKey: .photos)
    }

    required convenience public init(from decoder: Decoder) throws {
        guard let contextUserInfoKey = CodingUserInfoKey(rawValue: "context"),
            let managedObjectContext = decoder.userInfo[contextUserInfoKey] as? NSManagedObjectContext,
            let entity = NSEntityDescription.entity(forEntityName: "Pin", in: managedObjectContext) else {
                fatalError("Could not decode Pin!")
        }
        self.init(entity: entity, insertInto: managedObjectContext)
        let values = try decoder.container(keyedBy: CodingKeys.self)
        do {
            latitude = try values.decode(Double.self, forKey: .latitude)
            longitude = try values.decode(Double.self, forKey: .longitude)
            photos = NSSet(array: try values.decode([Photo].self, forKey: .photos))
        } catch {
            print(error)
        }
    }

    enum CodingKeys: String, CodingKey {
        case latitude = "latitude"
        case longitude = "longitude"
        case photos = "photos"
    }

}

Pin+CoreDataProperties.swift

import Foundation
import CoreData


extension Pin {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Pin> {
        return NSFetchRequest<Pin>(entityName: "Pin")
    }

    @NSManaged public var latitude: Double
    @NSManaged public var longitude: Double
    @NSManaged public var photos: NSSet?

}

// MARK: Generated accessors for photos
extension Pin {

    @objc(addPhotosObject:)
    @NSManaged public func addToPhotos(_ value: Photo)

    @objc(removePhotosObject:)
    @NSManaged public func removeFromPhotos(_ value: Photo)

    @objc(addPhotos:)
    @NSManaged public func addToPhotos(_ values: NSSet)

    @objc(removePhotos:)
    @NSManaged public func removeFromPhotos(_ values: NSSet)

}


回答1:


Declare photos as a swift native type

@NSManaged var photos: Set<Photo>

In decoder

photos = try values.decode(Set<Photo>.self, forKey: .photos)


来源:https://stackoverflow.com/questions/61882241/how-do-i-properly-use-codable-coredata-and-nsset-relationships

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!