Generic Types in NSManagedObject subclass - ios

I'm learning about CoreData and I have a rather basic question.
When I make an NSManagedObject subclass, the generated code in some places goes out of its way to make nicely typed functions for me, but in other cases leaves things annoyingly generic, and I'm confused why.
For example, I have a pair of managed classes with a parent/child, one to many (ordered) relationship. ClassA is the parent which can hold many instances of ClassB.
In the managed code generated for ClassA, it gives the container for the ClassB items as:
#NSManaged public var items: NSOrderedSet?
This is generic, but then later in the code there are lots of conveniently typed functions such as this one:
#objc(insertObject:inItemsAtIndex:)
#NSManaged public func insertIntoItems(_ value: ClassB, at idx: Int)
Great... but a fundamental one is missing: A strongly-typed accessor to get the items collection.
I can easily cast things of course:
var myItem = myObjectOfClassA.items[0] as! ClassB
But I'm confused... Why did they go out of their way to make nicely typed functions in lots of cases, but leave one of the most fundamental things generic?
I'm assuming there is something basic I don't understand here.

Because Swift and Foundation, that's why.
It's as generic as it's currently possible for this kind of relationship. It isn't more generic because
NSOrderedSet is not generic, so you can't have NSOrderedSet<ClassB>, for example.
Although Swift includes generic collections like Array and Set, there is no Swift-y generic OrderedSet collection.
There are also Swift-related gaps in the Core Data code generation. For example if this relationship wasn't ordered, the generated code would still use a non-generic NSSet. There's no good reason for that, but at least it can be cast to a Set.

Related

Upcasting NSObject to RLMObject

How would you upcast an NSObject to a RLMObject?
Say you have a model object that's of type NSObject, and say you wanted to dynamically upcast it to RLMObject, how would you do it?
It's worth mentioning that RLMObject's properties can't be populated at runtime, else I probably would've done it through <objc/runtime.h>. (I mean.. They technically can... It would just be too much of a hack)
What I'm trying to do:
The main purpose behind this is to create a caching framework that would dynamically choose between interchangeable caching dependencies such as Realm, SQLite, Core Data, etc. For example, I imagine having a preprocessor flag to hopefully switch from using Realm to SQLite, without having to change my models subclass.
This would require all of my models being a subclass of NSObject, simply because RLMObject wouldn't make sense in a SQLite environment for example.
I've been thinking about this a whole lot, and here's my approach:
1) Loop through the NSObject's properties at runtime & create a key/value object of them
2) Create a subclass of RLMObject at runtime and copy the property list from the passed NSObject model
3) Utilize Realm's initWithValue: to populate the properties
What's your opinion?
It looks like this method that you mention - RLMObject.initWithValue or a static equivalent createInDefaultRealmWithValue has to be called on an RLMObject subclass, or else it throws an exception: "Object type 'RLMObject' is not managed by the Realm".
You need a dynamic schema creation API (what underlies RLMObject), that I don't see being a public API.
An alternative would be to manually encode the object to some dictionary or NSData and attach it to a fixed RLMObject subclass.
You might lose some Realm features by not inheriting RLMObject like knowing when the object becomes dirty, but still probably get some success.
I think you'll get the same problem with Core Data. Normally Core Data supports only NSManagedObject subclasses, and moreover it requires you to define a fixed schema in advance in a model file (represented in code by NSManagedObjectModel).
Of course you could just treat your objects as dictionaries of property names and values, and place them into a giant ("type","id","property","value") table, but it is not the best approach (likely to be slow).
The same strategy is possible to implement with the SQLite backend. Interesting to see which schema would you choose for this.
I'd recommend to look at key-value stores as the backend for this, and avoid SQL. Or treat SQL as a key-value store, as in ("type+id", "encoded_object_data") :)

Cannot assign a value of type 'AnyDataSource<NSManagedObjectSubclass>' to a value of type 'AnyDataSource<NSManagedObject>'

I'm stumped.
The title of this question the compiler error.
I am creating an object that basically marries the delegates of NSFetchedResultsController and UITableViewDataSource. The type parameter is used to indicate which NSManagedObject subclass should be used in the fetch requests.
Here is an example where Swift lacks dynamism and we end up writing all sorts of crazy code, (OR, I'm new to Swift and dislike being told what I'm not allowed to do)
So, on a UITableViewController subclass, i'd like to have a property
var dataSource: AnyDataSource<NSManagedObject>?
when I try to create one of these with a subclass of NSManagedObject and assign it to that property, the compiler complains. There seems to be nothing I can do that will succeed without a warning.
You would think that I should be able to use NSManagedObject or any of its subclasses, so I'm a little confused.
Any ideas? I'm using typical "type erasure" patterns that can be found on the internet, like via realm.io.
Ultimately I found that this approach was not possible. I mean, to achieve these with completely protocol-based programming.
I defined a few base protocols with no associated type, implemented an abstract baseclass that implements that protocol, then introduced generic type parameters in subclasses, that implement further protocols that have associated type.
I'll post my generalized solution on github when it's finished.

Swift: why does inheriting from NSManagedObject ruin my properties?

I'm a total Swift/IOS newbie and there's something about CoreData that I simply can't understand.
I have a class with some non-optional properties that are initialized by a designated initializer. Then, if I set that class to inherit from NSManagedObject, then suddenly I get the error
Stored property X requires an initial value or should be #NSManaged.
Why does Swift suddenly think my properties are not initialized, even though they clearly are?
Also, I read that #NSManaged "tells the compiler that the storage and implementation of the properties will be handled by CoreData", but what does that even mean?
Any answers would be appreciated..
I was actually just reading about this yesterday.
Yes, it kinda really acts like #dynamic -- technically it might be
identical even. Semantically there is a slight difference:
#dynamic says 'compiler, don't check if my properties are also
implemented. There might be no code you can see but I guarantee it
will work at runtime'
#NSManaged now says 'compiler, don't check those properties as I have
Core Data to take care of the implementation - it will be there at
runtime'
so you could even say: #NSManaged is syntactic sugar that is a more
narrow version of dynamic :)
taken from this question
The big push with swift was to make the language extremely safe, as in this case, checking if the properties are implemented at compile time. If I understand correctly, CoreData doesn't quite conform to these compile time checks, thus adding in #NSManaged lets the compilers know that the variables will be taken care of.
From Apple:
You use the #NSManaged attribute to inform the Swift compiler that
Core Data provides the storage and implementation of a declaration at
runtime.

What does #NSManaged do?

I have encountered this keyword in various occasions. I kind of know what it's suppose to do. But I really want a better understanding of it.
What I noticed about #NSManaged - based not on documentation, but through repeated use:
It magically replaces key value coding.
It is roughly equivalent to #dynamic in Objective-C (which I don't know much about)
I need it to subclass PFObject from the Parse SDK. It normally uses KVC to read/write values from/to the backend.
Prefixing any variable with #NSManaged will shut the compiler up when I don't initialize within the initializer.
The formal definition (in the Core Data Apple Docs):
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.
What I got from that:
Variables with #NSManaged shall be exempt from compile time checks for something.
I've read the formal documentation and various other SO questions regarding this matter:
#synthesize vs #dynamic, what are the differences?
What is common case for #dynamic usage?
I instinctively recognize some scenarios where I should use it. I partially know what it does. But what I seek is purer understanding of what it does.
Further Observations:
A PFObject in the Parse SDK relies on Key Value Coding to access its values. The PFObject provides the following accessors:
objectForKey:
let score = results.objectForKey("descriptionOfResult")
//returns the descriptionOfResult value from the results object
setObject:forKey:
results.setObject("The results for a physics exam", forKey: "descriptionOfResult")
//sets the value of descriptionOfResult
To my understanding, #NSManaged magically understands that the variable I've declared automatically uses the above accessors to get and set. I'd like to know how it does that (if what I understand is true), and whatever else it does.
Yes, it kinda really acts like #dynamic -- technically it might be identical even. Semantically there is a slight difference:
#dynamic says 'compiler, don't check if my properties are also implemented. There might be no code you can see but I guarantee it will work at runtime'
#NSManaged now says 'compiler, don't check those properties as I have Core Data to take care of the implementation - it will be there at runtime'
so you could even say: #NSManaged is syntactic sugar that is a more narrow version of dynamic :)
https://github.com/KyoheiG3/DynamicBlurView/issues/2
here someone even used #NSManaged without CD because he wanted the #dynamic behaviour
In the apple docs, for Custom Managed Object Class, they quote properties example like...
To me it seems there is no difference, I have used #dynamic in objective C, it seems #NSManaged is the replacement in Swift.
Above mentioned answers are right. Here is my understanding.
#NSManaged indicates that the variables will get some values when we run the app. Coredata automatically creates getter and setter for such props. It silences the compiler for warnings.
NSmanaged is subclass of NSObject.
#NSManaged means extra code will be given to these props at runtime.
It tracks the changes made to those properties.

Programming methods in a non ARC Xcode project

I need to write some methods in a non ARC project in Xcode. I have to implement a NSXMutableDictionary class, a mutable dictionary that can contain up to four key-value pairs. The methods I have to implement are following:
- (void)setObject:(NSObject *)theObject forKey:(NSObject *)theKey;
- (void)removeObjectForKey:(NSObject *)theKey;
I have no clue how to do it, any help would be highly appreciated.
Thanks.
It's not a lot to go on.
But with what you provided, it's best to subclass NSObject and have a private property that is an NSMutableDictionary.
That allows you to implement all of the same methods of NSMutableDictionary just by declaring them, then in your implementation of each you just call the same method on your actual dictionary property.
The difference you add is a check to see if you already have 4 KVPs or not. And any additional methods you need or want.
This is the design pattern of Composition.
I agree with #uchuugaka. Create an object that has an NSMutableDictionary inside it. (This is a "has-a" relationship rather than an "is-a" relationship)
The NSDictionary family is what's known as a "class cluster". A class cluster is a public interface that's actually implemented by a set of private classes that you don't see.
Subclassing a class that is part of a class cluster is tricky, and not for beginners. There are a whole set of primitive methods you have to implement in order to create a subclass of a class cluster. Plus, your custom subclass will likely not preform as well as the original class because you won't adapt to different use-cases like the class cluster does.

Resources