Swift Core Data Usage in Xcode - swift

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.

Related

Swift Mirror Children collection empty when reflecting a type

I am trying to get a list of all properties on a struct.
I used this code:
struct MyBanana {
var b: String?
}
Mirror(reflecting: MyBanana.self).children.isEmpty // returns true
Why is my .children collection empty?
I need to be able to get these from a type rather than an instance.
I need to be able to get these from a type rather than an instance.
You can't. Swift's reflection story hasn't been fleshed out. The runtime metadata that's necessary for reflection has been present in Swift for a very long time. It's heavily relied upon by Xcode, the LLVM debugger and Instruments, but it was too frequently changing to make sense to build an API over it.
I expect reflection will be addressed somewhat soon, now that ABI stability has been established. Until then, are multiple third-party reflection libraries out there that you can use. Their authors had reverse engineered the runtime metadata, and built an API over it. You just have to make sure that the library has been updated since Swift 5.
Apple developer documentation says:
Mirror is a representation of the substructure and display style of an instance of any type. link
So you can apply mirror on instance. That's why children property is empty.
Mirror(reflecting: MyBanana()).children.count // return 1

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.

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.

Do #NSManaged variables have to be optional? [duplicate]

I have a core data entity named Film which has properties title and date. I noticed that the generated NSManagedObject subclass contains optional NSManaged properties even though I marked the properties as non optional in the core data inspector.
Can I can manually change it as non-optional property or is it a better choice to leave it as optional? Why?
"Optional" means something different to Core Data than it does to Swift.
If a Core Data attribute is not optional, it must have a non-nil value when you save changes. At other times Core Data doesn't care if the attribute is nil.
If a Swift property is not optional, it must have a non-nil value at all times after initialization is complete.
Making a Core Data attribute non-optional does not imply that it's non-optional in the Swift sense of the term. That's why generated code makes these properties optional-- as far as Core Data is concerned, it's legal to have nil values except when saving changes.
Update: After writing this answer I wrote a deep dive blog post explaining things in more detail: https://www.atomicbird.com/blog/clash-of-the-optionals/
This is a known issue. Some people change it to non-optional with no adverse effects, I keep it the way it was generated and hope for early fix.
It always helps if you submit a bug to Apple to increase visibility and priority.
Create managedobject class and change the entity class type to manual and add these classes to your project scope.
Edit your managedObject to make them non-optional. This means you need to maintain this class yourself and do any changes both in the core data model and the class
If your data model is stable and won't be changed then you can use this.
The Optional checkbox in the data model inspector has nothing to do with Swift optionals. The checkbox determines whether or not the attribute is required to have a value.
If you deselect the Optional checkbox for an attribute, you must give that attribute a value or you will get an error when saving. By selecting the Optional checkbox you can save without giving the attribute a value. Suppose you have a description attribute that's a string. If you select the Optional checkbox you could leave the description blank and still save the entity.
Here's another example. Suppose you have text fields to let a person enter their home, work, and cell phone numbers. These phone numbers should be optional attributes. You wouldn't want to require someone to have a home phone number, a work phone number, and a cell phone number just to save the person's data.

Add an extension/method to all objects in Swift

In Objective-C, all objects can be treated as type id, and nearly all objects inherit from NSObject. (Blocks don't, but that's about the only exception.)
Thus it's possible to create an Objective-C category that extends ALL Objective-C objects. (ignoring blocks)
In Objective-C, I created an extension to NSObject that uses associated objects to optionally attach a dictionary to any NSObject. That enabled me to implement methods setAssocValue:forKey: and assocValueForKey: that makes it possible to attach a key/value pair to any NSObject. This is useful in lots of circumstances.
It makes it possible to add stored properties to a category, just for example. You just write a getter/setter that uses the associated value methods to attach a stored object, and away you go.
It also makes it possible to attach values to existing system objects at runtime. You can hang data or blocks of code on buttons, or do whatever you need to do.
I'd like to do the same thing in Swift.
However, Swift does not have a common base class for all objects like Objective-C does. AnyObject and Any are protcols.
Thus,
extension AnyObject
Won't compile.
I'm at a loss as to where to "attach" my setAssocValue:forKey: and assocValueForKey: methods in Swift.
I could create a base class for my extension, but that defeats the point of using an extension. I could make my base object an Objective-C NSObject, but that means all my objects have to be NSObjects, and Swift objects are not NSObjects by default.
(BTW, this question applies to both the Mac OS and iOS platforms)
No. You've pretty much answered your own question--Swift objects don't have a base class, and the only real way to get around it is to inherit from NSObject.