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

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.

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.

I think you could look at attribute tab, there's optional="YES". It decided if that property is optional or not. You could changes by unchecked optional in Data Model insepector

Related

Swift core data: why do I need to unwrap when setting optional attribute values?

I have a core data model that contains mainly optional attributes. I assumed then that I would not need to unwrap the values that I am assigning to these optional attributes. e.g. I thought I would be able to do:
myEntity.gravity = currentOrder.gravity
(myEntity.gravity being optional)
However, Swift still requires me to unwrap currentOrder.gravity. I would have thought given that the variable I am assigning to is optional that I would not need to unwrap this.
Update:
Here is the definition of one of the core data entities I am describing:
<attribute name="percentComplete" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
The entity itself:
<entity name="AircraftMeasurementsCD" representedClassName="AircraftMeasurementsCD" syncable="YES" codeGenerationType="class">
It seems you're equivocating on the word "optional".
The word "optional" in the attribute description optional="YES" is not the same as, and has nothing to do with, the Swift Optional enum type.
The former is merely the ordinary English word "optional", meaning "not required" — for a particular entity, there might or might not be a value for this attribute, which is a Float, but even if there isn't, it's a valid entity. Nothing in the story says that this attribute's type is Optional<Float>. Its type is Float. And you can't assign a Swift Optional where its wrapped type is expected; you have to unwrap it.
Indeed, this point is made very explicitly by the documentation:
Optional attributes aren’t required to have a value when saved to the persistent store. Attributes are optional by default.
Core Data optionals aren’t the same as Swift optionals. [My italics.]

What are the functional differences between Coredata's CodeGen 'manual/none + create NSManagedObject subclass' vs. 'category/extension'

I've read Subclassing NSManagedObject with swift 3 and Xcode 8 beta and read this great tutorial. Still have questions on some points.
The similarities are:
I can customize both classes however I like.
I can add new attributes or remove or rename attributes. ie for category/extension it will get updated upon a new build (in the derived data), and in case of manual/none it will leave the class file intact and update the extension in the file navigation ie I won't end up with a duplicate file. This is all handled by Xcode because they are marked with a preprocessor #NSManaged
Dumping something like #NSManaged public var name: String? straight into an existing NSManagedObject subclass is not allowed. I tried to do entity.name = "John" but I got the following error: reason: '-[SomeEntity setName:]: unrecognized selector sent to instance 0x60400009b120'. I believe that's reasonable. I think without using the Core Data Model Editor the setter/getter accessor methods are not created.
The differences are:
For Category/Extension you just need to create the class yourself and add any extra functions/properties you need.
For Category/Extension the attributes are created in derived data which is enough. Because you never need to see that file. Its existence is enough to get things working.

And specifically in the context of making changes to your NSManaged properties:
Changing property type, e.g. NSDate to Date is allowed only for Manual/None . Example here
Changing optionality of a type, e.g. String? to String is allowed only for Manual/None. Example here
Changing a property access level, e.g. from public to private is allowed only for Manual/None. Example here
Having that said there is significant difference if I choose Manual/None codegen and but don't select 'create NSManagedObject subclass'. In that case I have start writing all the code myself (subclass from NSManagedObject and write NSManaged for every property)...or if I don't write all that code myself then I can still access/set fields using KVC which is awkward!
In a nutshell I'm just trying to figure out the full extent of capabilities that I can get from using Manual/None.
Question: Aside from the 9 notes which I need to know if I have validated correctly, an important question would be: how does me changing NSDate to Date or optional to non-optional not break the mappings between my NSManagedObject class and my object graph all while changing an NSDate property to String does break!! Does this have something to do with things that have guaranteed casting between Swift and Objective-C ie things that can be casted through as — without ? or !?
To address each of your notes and considering the cases where codegen is set to Manual/None and Category/Extension:
Yes, in either case you can customise the classes however you like (within limits - for example, the class must be a subclass - directly or indirectly - of NSManagedObject).
Correct. You can add, amend or delete attributes in the model editor. In the Category/Extension case, the relevant changes will be made automatically. In the Manual/None case, you can either manually update the Extension (or the class file) or you can redo the "create NSManagedObject subclass" which will update the Extension with the amended attribute details. If you do not do this, Xcode will not recognise the new attribute details and will not provide code completion for them (nor will it successfully compile if you try to override code completion). But unlike what you think this has nothing to do with the properties being marked as #NSManaged.
Correct. Adding an #NSManaged property to the class definition (or Extension) is enough to tell Xcode that the property exists (so you can reference them in code) but does not create the corresponding getter/setter. So your code will crash.
Yes, for Category/Extension just create and tailor the class file as you require.
Yes, for Category/Extension the properties are declared in the automatically created Extension file in Derived Data.
Changing the property definition in any way - from Date to NSDate, or marking it private, or whatever - can only be done in the Manual/None case because the Extension file in Derived Data is overwritten with each new build so any changes are lost.
Ditto
Ditto
Correct. You could write your app without ever creating separate NSManagedObject subclasses (automatically or manually), if you use KVC to access the properties.
As to your final point: you cannot arbitrarily change the type of the property definition: the type specified in the model editor must correspond to the type specified in the property definition. You can switch between optional and non-optional versions of the same type, and you can switch between Date and NSDate etc, but switching from Date to String will not work. I suspect you are correct that this is due to the bridging between Swift value type and the corresponding Objective-C reference type using as. See here.

Access control in swift 4

While upgrading to Swift4 from Swift3, I got some issues related to access control.
Here is the sample code. Which was there in Swift3, working fine in past times -
open class MyClass {
private let value: Int
static var defaultValue: Int { return 10 }
public init(value: Int = MyClass.defaultValue) {
self.value = value
}
}
To make the code run in Swift4, I have to change access control for defaultValue to public.
Here is the Swift4, compiling version
open class MyClass {
private let value: Int
static public var defaultValue: Int { return 10 }
public init(value: Int = MyClass.defaultValue) {
self.value = value
}
}
While I was wondering what is going on, I tried to remove open access control for MyClass, it allowed me to remove access identifier for defaultValue. Even can put it to private.
class MyClass {
private let value: Int
private static var defaultValue: Int { return 10 }
public init(value: Int = MyClass.defaultValue) {
self.value = value
}
}
I understand all the access identifiers, but I am not able to understand this behaviour. Especially the first case where xcode forced me to change access control of defaultValue to public.
Please help.
My original answer (shown below) is now mostly outdated – the beginnings of the resilience model are to be implemented in Swift 4.2 with the introduction of the #inlinable and #usableFromInline attributes, corresponding to the old #_inlineable and #_versioned attributes.
In addition, and more importantly, the rule for what default arguments of publically accessible functions can reference has changed again. To recap the previous rules:
In Swift 3 there was no enforcement of what access level such default argument expressions could reference (allowing your first example where defaultValue is internal).
In Swift 4, such a default argument could only refer to declarations exposed as a part of the module's interface, including those that aren't otherwise directly visible to users in another module (i.e #_versioned internal).
However in Swift 4.2, with the implementation of SE-0193, the rule is now that the default argument expression of a publicly accessible function can only refer to publicly accessible declarations (not even #inlinable internal or #usableFromInline internal).
I believe this is paving the way for the displaying of default argument expressions in a module's generated interface file. Currently Swift just shows an unhelpful = default, but I believe this will change to actually show the default argument. This can only realistically happen with this new access-control restriction in place (Edit: This is now happening).
Old answer (Swift 4)
This change is due to the work towards a resilience model that is already available via underscored attributes (#_inlineable, #_versioned, #_fixed_layout), but is yet to be officially finalised (so you probably shouldn't be using these attributes yourself yet). You can read about the full proposed details of the resilience model here, as well as the the Swift evolution discussion on it here.
In short, an inlineable function is one whose implementation, as well as declaration, is exposed as a part of a module's interface and can therefore be inlined when called from another module. An inlineable function must therefore also be publically accessible to begin with (i.e public or higher).
What you're running into is a change that makes default argument expressions for publically accessible functions inlineable, meaning that they must be available to be evaluated directly in the calling module's binary. This reduces the overhead of calling a function with default parameter values from another module, as the compiler no longer needs to do a function call for each default argument; it already knows the implementation.
I don't believe this change is officially documented in the release of Swift 4 itself, but it is confirmed by Swift compiler engineer Slava Pestov, who says:
Swift 3.1 added resilience diagnostics for inlineable code, which is not an officially supported feature, but in Swift 4 we switched these checks on for default argument expressions as well.
So if you have a publically accessible function with a default argument expression (such as MyClass.defaultValue in your case), that expression can now only refer to things that are also a part of that module's interface. So you need to make defaultValue publically accessible.
Unfortunately, there's currently no way to make a private function's declaration part of a module's interface (which would allow for your usage of it in a default argument expression). The attribute that would facilitate this is #_versioned, but it is forbidden with (file)private due to the following reasons given by Slava Pestov:
It would be a trivial change to allow #_versioned on private and
fileprivate declarations, but there are two pitfalls to keep in mind:
Private symbols are mangled with a ‘discriminator’ which is basically a hash of the file name. So now it would be part of the ABI,
which seems fragile — you can’t move the private function to another
source file, or rename the source file.
Similarly, right now a #_versioned function becoming public is an ABI compatible change. This would no longer work if you could have
private #_versioned functions, because the symbol name would change if
it became public.
For these reasons we decided against “private versioned” as a concept.
I feel like internal is enough here.
You could achieve this with a #_versioned var defaultValue though:
open class MyClass {
private let value: Int
#_versioned static var defaultValue: Int {
return 10
}
public init(value: Int = MyClass.defaultValue) {
self.value = value
}
}
The declaration of MyClass.defaultValue is now exported as a part of the module's interface, but still cannot be directly called from another module's code (as it's internal). However, the compiler of that module can now call it when evaluating the default argument expression. But, as said earlier, you probably shouldn't be using an underscored attribute here; you should wait until the resilience model has been finalised.

Is it possible to use optionals for members in structures or classes in Swift?

I'm having a difficulty reconciling my admittedly incomplete understanding of optionals and this from the Swift 2.1 documentation:
Classes and structures must set all of their stored properties to an
appropriate initial value by the time an instance of that class or
structure is created. Stored properties cannot be left in an
indeterminate state.
I'd like to be able to do something like:
struct Name {
var firstName = "Flintstone First"
var middleName: String?
var lastName = "Flintstone"
}
var wilmaHusband: Name
wilmaHusband.firstName = "Fred"
where middleName may be nil. However, quite understandably according to that part of the Swift documentation, I encounter the error Struct wilmaHusband must be completely initialized before a member is stored...
Is what I'm trying to do--make a member potentially nil in a structure (or class) impossible in Swift? If so, it seems one seeming advantage of optionals--that they may hold the kind of data that may or may not be present in a structured object (such as a middle name in a name construct)--is lost.
I feel like I'm missing something fundamental here in my understanding of optionals, and I apologize in advance for my naïveté.
Since all members of the struct have an initial value
(including var middleName: String? which – as an optional – is
implicitly initialized to nil), you can create a variable of that
type simply with
var wilmaHusband = Name()

CoreData Handling Sets

I'm introducing CoreData to an existing class with objects that include to-Many-Relations.
Before I would have for example something like this:
"Player" is just a normal class with a result array...
for (y,result) in player.result!.enumerate() {
Now i would have to do it like this....
for (y,result) in (player.relResults!.allObjects as! [CDResult]).enumerate()
Is there a better way to do this?
Is there a way to avoid this ugly casting stuff?
An optional to-many relationship is represented in the NSManagedObject
subclass as an optional NSSet:
#NSManaged var results: NSSet?
So the type of the target entity of the relationship is lost here,
and some casting is needed. But you don't have to convert
the set to an array. You can cast it to a Set of the appropriate
type and iterate over the set:
for result in player.results! as! Set<CDResult> { ... }
Note that player.results can be nil, so this should be checked
before, or wrapped into an optional binding with if let or
guard let.
You could also change the property definition to
#NSManaged var results: Set<CDResult>?
and then simply iterate with
for result in player.results! { ... }
The disadvantage is that the change is lost if you re-generate the
managed object subclass in Xcode. Perhaps a future version of
Xcode will create Set<T> typed properties for to-many relationships.
(You may also have a look at "mogenerator" which is an alternative tool
to create the managed object subclasses with some nice additional features. It could be that mogenerator handles this better.)