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

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.

Related

Swift Core Data Usage in Xcode

I am relatively new to Swift Programming and recently tried out Core Data for the first time. However, I am having a hard time understanding several (for me) strange behaviours I am encountering:
I have create all my entities and their attributes in the ".xcdatamodeld" file. Codegen is on "Manual/None". Some of the attributes are marked a non-optional. Still, when I generate the NSManagedObject Subclass files, I still see them as optional in the "...Properties" files, i.e. having a "?" after the type. Why is that?
Somewhat relating/in contrast to the first bullets, the attributes for some entities do not have the "?", although they are marked as optional. When I try to add the "?", I get an error "Property cannot be marked #NSManaged because its type cannot be represented in Objective-C". Why is that?
When I'm creating the NSManagedObject Subclasses and select some subfolder in my project for them to be created in, they are put at the top of the hierarchy tree, regardless.
What happens if I change information in the "...Class"/"...Properties" files generated by Core Data which would conflict with what is in the ".xcdatamodeld" file. What takes precedence? How are they related?
In general I find there is not much detailed descriptions available on Core Data except introductory things. Would anyone know some good resources on that? Website? Youtube Videos? Books?
Answers:
Creating the subclasses manually treates the optionals not accurately. Check any attribute and remove the question mark in the class if it's non-optional in the model.
Scalar Swift optional types (Int?, Double?, Bool?) cannot be represented in Objective-C. I recommend to declare them as non-optional.
Never mind, it has no effect where the classes are located, the main thing is that the file name is black (valid) in the Project Navigator and the target membership is assigned correctly.
in the Codegen Manual / None case you are responsible that the types in the model match the types in the classes otherwise you could get unexpected behavior. Any change in the class must be done also in the model and vice versa. However you can replace suggested ObjC classes like NSSet or NSDate with native Swift types Set<MyClass> or Date without changing the type in the model.

Stop Matlab from using set methods when loading matlab.mixin.Copyable object?

I have a classe derived from matlab.mixin.Copyable. Some of the properties have set methods. I want the setting of those properties to also update other properties so that the relationship between the parameters are consistent (a classic use of set methods).
Unfortunately, when I load a *.mat file [specifically using, say, x=load('file.mat')], setters are also used. There should be no need for that kind of automatic update of multiple parameters, since all the object's properties can be copied from the *.mat file and self-consistency is automatically maintained. Instead, using setters during load causes errors because the setter uses other properties that haven't yet been assigned to in the load process. I see this from error that occurs during the load, and from checking the properties that are needed by the setter.
Is there any way to force the load to do a simple replication of the property values contained the *.mat file? Some of the properties are complex objects themselves, so what's needed is a recursive copy-by-value during load. It seems rather inappropriate to use setters during a load, for the reasons above.
P.S. I say above that the setter uses another as-yet-unassigned property. Let's call this property p2. It also gets assigned to by a setter for 3rd property s1. It seemed odd, but s1 does have a value, while p2 does not. One possible reason is that p2 relies on other properties in addition to s1, and those might not have been assigned to when s1 is loaded (i.e., when the s1 setter is invoked). The whole problem stems from the fact that load occurs outside of the context and the order in which properties are assigned to during the execution of the code that created it. This is the key reason why (it seems to me that) load should not use setters. Otherwise, it seems to be incompatible with either copying or loading (I'm not sure which at the moment -- maybe both).
The process MATLAB uses to load objects is well documented. This page mentions, among many other things, that the set methods are called to prevent issues that happen when a class definition changes, and you try to load an object from that class from an old file.
There is also a page explaining how to modify the save and load process. This page describes the use of loadobj, defined as a static method to your class, to change how the object is constructed from the information in the file. loadobj is called when one of the set methods throws an error; but it is always called if saveobj returns a struct. That is, one solution to your problem is to have saveobj create a struct with all the data, and have loadobj reconstruct your object from that struct.
This other documentation page describes how to design your class to avoid property initialization order dependency -- which is the problem you describe having. The way to do this is to have all public properties be dependent properties, with the "actual" data properties being hidden and not interdependent. You might be able to construct your class this way, for example with a single data property (a cell array or struct), which would be loaded and saved in one go, and the public properties simply using a portion of this cell or struct in the set and get methods.

Best Practices: description vs debugDescription

Headline: description called by super.init()
This is a new take on an old question. As a primarily Swift programmer I tend to not use NSObject for class definitions because of the residual side effects of Objective-C. Like if I have a read-only property called length and I then want to create a setter function called setLength, I get warnings about it conflicting with a similar definition from Objective-C. I just discovered the set(var){} setter. If I subclass a Cacoa class like UIDocument, etc. that inherit from NSObject, I have to live with these side effects.
I have a class that uses two other classes in the property definitions, none of them NSObjects. This class has a description computed variable that uses the description computed variables for the other two classes in its composition. All three classes need to conform to the CustomStringConvertable protocol. Ok, everything is good.
At some point this class got upgraded to being a UIDocument and the CustomStringConvertable became redundant and was removed. Everything still works.
Here is what I found out today. I wanted to break at a point in the program where it was printing one of the two properties and as a convenience I set the break point in the description variable for that class, thinking that it should only be called at the point I am interested in, where it is printed out. What I discovered is that the description variable gets called during all the super.init() of the UIDocument sub-class! And there were a few of them. I think composing strings as being relatively expensive but didn't care because they were only used in debug, but with them being called and who knows how they are used in super.init(), I need to change this.
I checked another UIDocument class in the same program that has 200 files associated with it and it is also calling description in super.init().
Does anyone have any input on the Best Practices for using description vs debugDescription?
I'm going to answer my own question as a matter of documentation.
I switched the UIDocuments subclasses to define and use debugDescription. I am debugging some code that loads all the files and does some manipulation and I was able to reduce the load time from 9.8 seconds to 6.8 seconds.
I also went through all the places where the Swift 3 conversion added String(describing:) to the program and found I could change a lot of them to using debugDescription and eliminate the String(describing:) wrapper.
I think the best practice is to only define and use debugDescription and for my non-NSObjects change conformance from CustomStringConvertable to CustomDebugStringConvertable.

dynamic setting and getting values from Swift class

I'd like to copy all properties from a NSManagedObject over to a "regular" Swift class. I don't want to do this manually, i.e. make a regular class with all the properties for every NSManagedObject and then manually copy all those values.
I do know how to read property names and values dynamically from my managed object, but how to set them on a Swift class in a way that I can then use those values like
mySwiftObject.name
which returns a String or
mySwiftObject.age
which returns a Number (as those are the types on the Managed Object). Custom subscripting and stuff like that came to my mind, but I didn't manage to achieve this... Is there a nice way to do exactly that?

Why does adding 'dynamic' fix my bad access issues?

I'm having a strange issue that appeared with iOS 8 Beta 5 (this issue did not occur with previous versions).
I tried to create an empty project and try to replicate the issue, but I'm unable to do so, so I'm not quite sure where the issue lies.
What I'm seeing is that attempting to access methods of a custom NSManagedObject subclass results in a strange EXC_BAD_ACCESS error.
For example:
var titleWithComma: String {
return "\(self.title),"
}
This method, out of many others, causes this issue when called. However, adding a dynamic keyword before it makes the issue go away:
dynamic var titleWithComma: String {
return "\(self.title),"
}
I know I'm not giving enough info, because I honestly don't know how to pinpoint the actual issue, but can anyone explain what is possibly happening, and why adding dynamic might resolve this issue?
From Swift Language Reference (Language Reference > Declarations > Declaration Modifier)
Apply this modifier to any member of a class that can be represented
by Objective-C. When you mark a member declaration with the dynamic
modifier, access to that member is always dynamically dispatched using
the Objective-C runtime. Access to that member is never inlined or
devirtualized by the compiler.
Because declarations marked with the dynamic modifier are dispatched
using the Objective-C runtime, they’re implicitly marked with the objc
attribute.
It means that your property/method can be accessed by Objective-C code or class. Normally it happens when you sub-classing a Swift class of Objective-C base class.
This is from the prerelease Swift / Objective-C interoperability documentation:
Implementing Core Data Managed Object Subclasses
Core Data provides the underlying storage and implementation of properties in subclasses of the NSManagedObject class. Add the #NSManaged attribute before each property definition in your managed object subclass that corresponds to an attribute or relationship in your Core Data model. Like the #dynamic attribute in Objective-C, the #NSManaged attribute informs the Swift compiler that the storage and implementation of a property will be provided at runtime. However, unlike #dynamic, the #NSManaged attribute is available only for Core Data support.
So, because of some of the Objective-C runtime features that Core Data uses under the covers, Swift properties need to be specially annotated.