Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Core Data codegen decide whether to make a property optional?

I am working with Xcode 10 + Swift 4.2 on macOS 10.13.6 High Sierra. I have created a data model and I am letting Core Data automatically generate classes for the data model entities. For the most part this works as expected. However, I cannot figure out how to predict whether some properties in the generated classes are going to be Optional types; it does not appear to depend on whether the corresponding attributes were declared as "Optional" (i.e. "Optional" check box is checked in the description of the attribute). Can someone help me figure out how Xcode figures out whether to make a class property optional or not?

Here is a small example which I've derived from my project. I just replaced the project-specific names with made-up names, but everything else is the same. First here is an extract from the data model for the description of the Foo entity:

<entity name="Foo" representedClassName="Foo" parentEntity="FooParent" syncable="YES" codeGenerationType="class">
  <attribute name="v1" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
  <attribute name="v2" attributeType="Boolean" usesScalarValueType="YES" syncable="YES"/>
  <attribute name="v3" optional="YES" attributeType="Boolean" usesScalarValueType="YES" syncable="YES"/>
  <attribute name="v4" optional="YES" attributeType="UUID" usesScalarValueType="NO" syncable="YES"/>
  <attribute name="v5" optional="YES" attributeType="String" syncable="YES"/>
  <relationship name="v6" toMany="YES" deletionRule="Nullify" destinationEntity="Bar"
                inverseName="baz" inverseEntity="Bar" syncable="YES"/>
</entity>

As you can see, v1, v2, and v6 are not declared optional in the data model, while v3, v4, and v5 are declared optional. Here is the code that was generated, which I found in Foo+CoreDataProperties.swift somewhere under the Xcode/DerivedData folder for my project.

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

    @NSManaged public var v1: Date?
    @NSManaged public var v2: Bool
    @NSManaged public var v3: Bool
    @NSManaged public var v4: UUID?
    @NSManaged public var v5: String?
    @NSManaged public var v6: NSSet?

}

As you can see v2 and v3 are not optional, while v1, v4, v5, and v6 are optional, and that's a different assignment of optional/non-optional than what was seen in the data model.

Can someone help me understand how Core Data decides whether a generated class property should be optional or non-optional?

On the one hand, I find myself writing v!.something where I would hope that I can write v.something because v is non-optional in the data model, and therefore there will certainly be some value present (assuming Core Data is enforcing the non-optional attribute).

And on the other hand, I want to write let v = v { ... } for a property v which is optional in the data model, so there might or might not be a value present. Xcode is giving me an error about that construct, but I can't omit a test -- I already know that some instances of v will be absent. Attempting to work around by writing if v != nil { v! } fails -- Xcode gives an error about not being able to unwrap a non-optional value (and also a warning about comparing a non-optional to nil).

Thanks in advance for any light you can shed on this.

EDIT: For the last bit about a non-optional property corresponding to an optional attribute, I see that I can just omit the ! and write if v != nil { v } -- of course the warning about comparing non-optional to nil is still there, but the error goes away. So that's a workaround.

like image 541
Robert Dodier Avatar asked Nov 17 '22 14:11

Robert Dodier


1 Answers

Core Data is still rooted in Objective-C. So the primitives (bool, int, float, etc.) will be non-optionals, and the objects will be optionals.

Some data can be either an object or a primitive. For example, someone's age could be a NSNumber or a NSInteger. If you prefer that such a variable be backed by a primitive, then you can check the scalar box for the property in your model.

like image 125
Jeff Wolski Avatar answered Dec 31 '22 11:12

Jeff Wolski