Swift - CoreData’s NSManagedObject subclass - ios

When creating NSManagedObject subclasses in Swift, I get an error:
2015-02-12 00:12:57.662 MyApp[1934:272844] CoreData: warning: Unable to load class named ‘ClassName' for entity 'ClassName'. Class not found, using default NSManagedObject instead.
I can fix this by manually adding #objc(ClassName) in the Swift subclass, but not only does this defeat the purpose of automatic subclassing, but I don’t want to waste my development time adding this to all two dozen NSManagedObject subclasses.
Originally I did this and just got over it. But this time I needed to change my model and generate all subclasses again and have a deadline coming up, so what would be the correct way to overcome this error so regenerating my model’s subclasses doesn’t mean wasting so much time/patience.
Thanks as ever

You are out of luck. Some things in Swift just do not (yet) work the way they used to in Objective-C.
Consider writing a script (in Perl, Python - or Swift!) to insert the missing line. For example, the first line of the class declaration is always on the same line, so it should be trivial to extract the class name and insert the corresponding #objC call.

Related

"Cannot define category for undefined class" error in Xcode 11.3, no access to source file

I'm trying to make a category for a class that gets defined in a source file I don't have access to, namely RunnerViewController.
The two important files here are iPad_RunnerAppDelegate.h and FilesBrowser.mm. I do not have access to the header file's corresponding source file.
iPad_RunnerAppDelegate.h contains a forward declaration to the RunnerViewController class, and can reference that class with no difficulties.
However, if I try to #include "iPad_RunnerAppDelegate.h" in FilesBrowser.mm and then try to create a category in the same file, it doesn't recognise the class name.
Despite this error appearing, I can still use the RunnerViewController class inside FilesBrowser.mm, I just can't make categories for it.
What's happening here, and how would I fix it?
I've had to do this same thing but it was a long time ago. The problem is that without some indication of where to apply the category, your code cannot work alone. What you need is info to the compiler to let it know where it's going to insert the category.
In FilesBrowser.mm, you will tell it by adding this BEFORE "iPad_RunnerAppDelegate.h":
#interface RunnerViewController: UIViewController // or whatever it actually subclasses
#end
Now the compiler knows that its going to insert the category against an Objective C class named RunnerViewController.
But - you're not completely done. Now you need to insure that the runtime / loader do the class loading and category insertions at the proper time. This is where my knowledge gets a bit fuzzy, so it may not turn out to be a problem at all.
What I believe needs to occur is for the RunnerViewController class to get loaded first, then later at some point the category you wrote applied to it (but this may not be necessary). So if RunnerViewController is statically linked into your app (using a .a archive), then you're good for sure. But if that class is in a dylib loaded dynamically, and your category is statically linked - well that might be a problem. I just don't know how long Apple waits to try and deal with the categories (which by the way the -ObjC link flag is for.
In any case try the code above first and just see what happens. If RunnerViewController is dynamically loaded, and you get some hard runtime error, there is a workaround where you would make your category go into a module/framework that is also dynamically linked, but gets linked after RunnerViewController.
In the end I believe you have a good chance of making this happen.

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.

What is the use of "filename+CoreDataProperties.swift" and "filename.swift" NSmanagedObject subclasses?

I recently noticed while creating the NSManagedObject subclass two classes are getting created.
One is filename+CoreDataProperties.swift and other is filename.swift.
Is filename+CoreDataProperties.swift similar to filename.h and filename.swift to filename.m in Objective-c? If so then i can put my implement my logic inside the filename.swift using the +CoreDataProperties.swift class.
I seriously couldnt find out the differences between two these files and whats their use?
Any help is appreciated
I think the filename+CoreDataProperties.swift version gets overwritten every time you export your model from Editor > Create NSManagedObject Subclass (e.g., every time you update your model and need to re-export the class files).
If you have some other, non-Core Data properties (or methods) in your custom subclass, they would be lost every time.
By separating the Core Data-specific code and your custom code, you can export from the Core Data editor as many times as you want without losing your non-Core Data additions.

Why isn't there a default implementation of NSCoding?

I understand how to use NSCoding to convert my objects to archive objects. That's not my question.
What I'm wondering is why there isn't a default implementation of NSCoding that could handle probably 99% of cases.
For instance, every time I write a custom class that I want to archive, I perform the following:
Implement -(void)encodeWithCoder: and -(id)initWithCoder:.
Go down my property list, writing a pair of statements (one encode, one decode) for each property.
If the property is an object, I use the encode/decodeObject method.
If the property is a value, I use the corresponding encode/decode method.
I always use the property's name as my key.
I would suspect that almost every implementation of NSCoding is exactly like mine, with the only changes being the particular properties that need to be manipulated.
It seems to me that this would be a perfect place for a standard implementation, with the option to override if your particular case if funky.
Do I have a misunderstanding of what's going on? If not, could I add a category on NSObject to implement this common method on all objects in my projects?
I suspect that the answer to your question is simply that NSCoding was designed long before Objective-C properties existed. (NSCoding was part of the OpenStep spec in 1994, whereas properties arrived with Objective-C 2.0 in 2007.) Additionally, some classes have properties that are not appropriate to be serialized for later.
However, your proposed solution could be a great time-saver! At least one such solution already exists. Check out AutoCoding.

Showing a UIView subclass - this class is not key value coding-compliant for the key

I am trying to create a custom popup view that can be called from multiple view controllers, but im having some trouble.
I'm able to get it to work fine as long as I write and call a "presentPopup" method from within the viewController itself. Rather then writing an individual method in each VC, i'd much prefer to write a method in a separate class and just pass parameters to personalize it.
Anyway, whenever I try to do so, I keep getting the famous "this class is not key value coding-compliant for the key" error. Just wondering if anyone had any insights as to HOW to make the class key value coding compliant? Or how to go about this in general?? Thanks!!
There is a simple explanation at the end of this answer, but I've seen a few similar questions recently so I thought I'd give a bit of background.
The error should also be telling you which key the class is not key value coding compliant for. The phrasing of your question suggests that you think there is some general bit of code you can add to make a class "key value coding compliant". This isn't the case.
All cocoa / cocoa touch objects are capable of performing key value coding operations. KVC allows you to reach accessor methods by using valueForKey: or setValue:forKey: instead of using the accessor methods directly.
The error you are seeing will be along the lines of:
XXX - this class is not key value coding compliant for key YYY.
XXX is the class in question, YYY is the key. So somewhere, [xxx setValue:something forKey:#"YYY"] is being called.
At this point, you're thinking "but I've never used setValue:forKey in my code!". You may be right. But it is used by the frameworks when you load a xib file - all the outlets are set using key-value coding.
So, you will have an outlet in your xib that is connected to something that has since been removed or renamed in the class it links to. If you're lucky, it will have a little exclamation mark next to it. If you're not, you won't even see it in interface builder and you'll have to edit the xib as source code and remove it from the XML.
You are calling setValue:forKey: method somewhere (probably, on a NSMutableDictionary where you should call setObject:forKey) or something similar...

Resources