5

I am trying to use a custom class with swift and CoreData as a transformable object. I have been wasting hours trying to figure it out but cannot. I keep getting two errors: Property cannot be marked @NSManaged because its type cannot be represented in Objective-C and Property cannot be declared public because its type uses an internal type.
Help is greatly appreciated. Thanks.

I have a CoreData object named User. I also have an object named Site.
I don't want the Site object to be a CoreData object because the majority of the times that I create a Site object I don't want it added to the CoreData context.

Below you will see "primarySite" of type "Site" and "sites" which is an array of type "Site". These two lines give the same two errors I listed above.

Data Model Data Model

User+CoreDataClass

import Foundation
import CoreData

public class User: NSManagedObject {
}

User+CoreDataProperties

import Foundation
import CoreData

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

    @NSManaged public var firstName: String?
    @NSManaged public var lastName: String?
    @NSManaged public var role: String?
    @NSManaged public var primarySite: Site
    @NSManaged public var sites: [Site]
}

Site

import UIKit

class Site: NSCoding {
    let identifier: Int32
    let name: String
    let note: String

    init(with values: Dictionary<String,Any>) {
        identifier = Int32(values["identifier"] as! String)!
        name = values["name"] as! String
        note = values["note"] as! String
    }

    required convenience init(coder decoder: NSCoder) {
        var siteValues = [String:Any]()
        siteValues["identifier"] = decoder.decodeObject(forKey: "identifier") as! String
        siteValues["name"] = decoder.decodeObject(forKey: "name") as! String
        siteValues["note"] = decoder.decodeObject(forKey: "note") as!
        self.init(with: siteValues)
    }

     func encode(with coder: NSCoder) {
        coder.encode(String(self.identifier), forKey: "identifier")
        coder.encode(self.name, forKey: "name")
        coder.encode(self.note, forKey: "note")
    }
}
King Tech
  • 314
  • 1
  • 3
  • 12
  • 1
    Unless your Site is actually more complex than your code snippet implies, I would add it as a new entity, and define primarySite and sites as relationships from User to Sites, rather than transformable attributes. – pbasdf Jan 15 '18 at 09:15
  • You show that `User` is using manual/none code generation, and you have an extension on `User`. Where is the `User` class defined? – Tom Harrington Jan 15 '18 at 15:46
  • User has two files generated by Xcode. User+CoreDataClass.swift didn't contain anything other than the class declaration so I didn't think to include it. – King Tech Jan 16 '18 at 13:24

3 Answers3

4

I figured out how to resolve both errors.

1) "Property cannot be marked @NSManaged because its type cannot be represented in Objective-C"
     Thanks to @Tom's answer I resolve this error by making Site a subclass of NSObject.

class Site: NSObject, NSCoding {


2) "Property cannot be declared public because its type uses an internal type"
     I resolve this error by making adding "public" before the class, and the encoder and decoder functions.

public class Site: NSObject, NSCoding {
...
public func encode(with coder: NSCoder) {...}
public required convenience init(coder decoder: NSCoder) {...}
King Tech
  • 314
  • 1
  • 3
  • 12
3

Property cannot be marked @NSManaged because its type cannot be represented in Objective-C

This happens because Site does not inherit from NSObject. Swift classes don't have to inherit from NSObject, but Core Data transformables do. If you add NSObject, that error doesn't occur.

There are some other problems-- your init(coder decoder: NSCoder) doesn't assign any property values, for example-- but those should be easy to figure out.

Tom Harrington
  • 69,312
  • 10
  • 146
  • 170
  • I forgot to copy of the line in init(coder decoder: NSCoder) that call self.init(). I edited the question. Thanks. – King Tech Jan 16 '18 at 11:38
0

Since Site is a custom class, you cannot use it as a datatype in your NSManagedObject.

In my case, I made their type "Binary Data" in your Data Model. Thus, making its type NSData which is a data type in objective c.

After this, you can unarchived the data.

For example:

if let sitesArray = NSKeyedUnarchiver.unarchiveObject(with: sites) as? [[String : Any]] {
    //enter your processing code here to convert them to site objects
}
Ace Rivera
  • 620
  • 4
  • 14