Being fairly new to CoreData (Started playing with it 2 hours ago ;) ) I keep wondering if there is an easier way to do things.
I'm currently using a DataModel to create my persistant objects code.
However when I keep adjusting things like number of attributes sotred, the name of the fields etc... and that leads to having the update the code of my NSManagedObjects.
I found that you can create the classes with the attributes already implemented, however, I haven't figured out how to update the code of an existing class other than adding it by hand or creating a new object and overwriting the existing one? (or how do other developers do it)
I'm aware that the answer is surely: "there is no better way" but I still have to ask.
Check out mogenerator.
This command line utility will generate two class files. A machine generated _MyClass header and implementation, with all code for your model. It will also create a subclass MyClass where you can add your own customizations.
Each time you regenerate your code, only the machine generated _MyClass files will be updated. All your changes to MyClass will be preserved.
If you can not use mogenerator, you can use the native code generation. Make sure you regenerate the code every time. However, that will erase any changes.
So, you have two very viable options.
Write a category on the entity class. You can do just about everything you want with a category.
Subclass from the entity, and put all your code in there.
Personally, I prefer using categories unless I absolutely must inherit... which is extremely rare.
Related
Using Version 8.1 of Xcode.
Create an entity named "MapRegionObject" in .xcdatamodeld file.
Using auto code generator, click Editor on the navigation bar -> create NSManagedOject Subclass...
Got two files : MapRegionObject+CoreDataClass.swift and MapRegionObject+CoreDataProperties
Errors in two files showing in the screenshot:
MapRegionObject+CoreDataClass.swift
MapRegionObject+CoreDataProperties
Please help me fix this bugs, thank you so much!
In Xcode 8.1, before using the auto code generator, you must select the entity in your data model:
Then go to the Data Model Inspector tab:
Under "Codegen" select "Manual/Node"
After that you could create a NSManagedObject subclass without errors.
Alternatively, if you have already used 'Class Definition', you can go into your existing .xcdatamodeld file and set all current entities to 'Manual/None' under Codegen. Make sure to save your project (File -> Save), delete your existing Derived Data, clean the project, and then build. Resolved it for me without having to re-make my whole model.
I found this whole thing to be very confusing. You really do need to understand what's new in CoreData. Basically, the default is to automatically generate the class and the extensions for you in a place called "DerivedData" that's buried in ~/Library/Developer/Xcode/DerivedData where these classes and their extensions live, outside of your code source. Personally, not being able to open and look at them is weird to me, but something to get used it.
Basically, if you have an entity called "AppSettings" in your CoreData model, you can just use it without needing to generate the code yourself. If you want the code in your project, then set the Codegen property on the entity to Manual/None. Then do what you did before: Editor->Create NSManagedObject classes, etc. The files will wind up in your project.
The good news is that if you want to make custom extensions, just do that in your project. Xcode will blend the generated files, from their other place outside your project directory, with the files in your project directory.
1) clean the project (cmd + shift + K)
2) In the "data model inspector" for every created Entity set attributes for Class just as in the screenshot below
3) Generate code again (Editor -> create NSManagedObject Subclasses)
After that everything should work fine.
The problem is that you don't need to generate NSManagedObjectModel subclasses manually anymore.
ref: https://forums.developer.apple.com/thread/48988
Xcode automatically generates classes or class extensions for the
entities and properties in a Core Data data model. Automatic code
generation is enabled and disabled on an entity by entity basis, and
is enabled for all entities in new models using the Xcode 8 file
format. This feature is available for any data model that has been
upgraded to the Xcode 8 format. You specify whether Xcode generates
Swift or Objective-C code for a data model using the data model’s file
inspector. When automatic code generation is enabled for an entity,
Xcode creates either a class or class extension for the entity as
specified in the entity's inspector: the specified class name is used
and the sources are placed in the project’s Derived Data. For both
Swift and Objective-C, these classes are directly usable from the
project’s code. For Objective-C, an additional header file is created
for all generated entities in your model: The file name conforms to
the naming convention 'DataModelName+CoreDataModel.h'.
Close the project and follow the following instructions:
Reveal in finder your database .xcdatamodeld file.
on .xcdatamodeld file right click -> Show Package Contents, if(.xcdatamodel) is find again in the package again right click and 'Show Package Contents'. you should get 'contents' file.
Open 'contents' in text edit.
Command-F (codeGenerationType="class") and replace all matchings string with blank string.
Save and open Xcode project again.Everything should work well.
In Xcode 8.2.1 , Menu-Product-Clean,And every is ok, it's so verid.
Don't fight the with Xcode on this unless you really need to alter your generated properties, doing so will just leave you frustrated.
Think of the auto-generated class as any other class in your application. If you need to add functionality to your managed object class, simply change your class definition into an extension and extend your object.
change your class:
class MyManagedObject : NSManagedObject { /* implementation*/ }
to an extension:
extension MyManagedObject { /* implementation */ }
This isn't an answer. It's just explanation of what's going on with the selections
Make sure you see this moment for the Core Data Stanford course
Below is a transcript I wrote myself (It's not 100% accurate):
The default is class definition, if you choose this one. It will
generate that subclass and it will just work. You'll be able to access
your tweets as a class called Tweet. This sounds good. Btw if you do
this it will NOT show up in your file navigator.
The one we choose the most often is the category/extension what this
will do it will only generate an extension of the Tweet class. You
have to actually write the tweet class itself. The extension will take
care of making all the vars. Even when I switch to category/extension
again I don't get that extension showing up in the navigator. It's
kind of hidden from you.
And why do we like this one? Because a lot of times we want to add our
own code. Like in a Tweet, imagine you want to add a static method
that took data from Twitter and turned it into a tweet in the
database. Where would we put code? Well a great place to put that code
would be in the Tweet class...if there was such a thing...and the
extension is going to handle all the var business for you.
If you did choose manual/none for codegen. meaning don't do any
codegen, then you would be doing value/setValue(forKey:)...good luck
with that You're code is going to be a mess. [ie there is no
.propertyName = value ...you'd had to do setValue(value, forKey:
propertyName)].
Long story short, I'm not sure why but for some reason if you don't select the create NSManagedObject subclass then it seems to still work, but without showing what's happening under the hood. Very counter intuitive!
Then also watch this live demo of the Core Data Stanford course:
Now we know we want to access all this stuff not using
value/set(value:forKey:)...we want to have to subclasses of
Users/Tweets. And we want to have vars [ dot notation] for all these
relationships so we need that code to be generate. The way we do that
we just select the entity... and we go down here to CodeGen. This says
by default class definition. That means it's done it. It has generate
a class called Tweet. and It's going to work with var and all
relationships. That's not actually what we want. We want to select
this one [Category/Extension]. Where only create an extension to Tweet
and add the var stuff. That's because we want to write the class
Tweet and put our own code in there. It's very common to write our
own class. But you still want the var magic.
I have what feels should be a simple problem, but cannot find a simple answer.
I have a simple migration, I just need to add a synthentic property for use in a Fetched Results Controller. This new property is just a BOOL (used for sorting) that is derived from another property.
Example:
var title: String? // "engineer" | "accountant"
var hasTitle: Bool // title != nil
Simply, I need to add the new field hasTitle (which seems trivial), and the populate it once. I want to keep this code out of my normal app logic, so I don't have to code in a history of all my schema changes.
Is it possible to do a lightweight migration followed by an isolated, one-time mass update, or do I have to do a custom / heavyweight migration.
And secondly, if I need to do a custom migration, are there any mitigating techniques - can I use a Value Expression "Function" to do initialize the above (title != nil)
What I want to avoid is having to set a NSUserDefaults flag for a migration, and check that on every launch. I would like to contain the complexity of the migration to migration specific code and not pollute the regular app logic.
Thanks!
Is it possible to do a lightweight migration followed by an isolated, one-time mass update, or do I have to do a custom / heavyweight migration.
Both are possible. I'd go with a custom migration, simply because it'll save you from having code that needs to check on the new attribute every time the app launches. For a simple change like yours it's pretty straightforward, although it's uncharted territory for most iOS developers. I recently described the process in detail in another answer.
If you don't go with a custom migration, you'd need to make the new attribute optional and then add some custom code to check if a value exists and assign one if it doesn't. It might seem simpler, but that's just because it probably seems more familiar. Core Data will save you from needing to insert this kind of check in your app launch process, and ensure that your conversion code runs only when a conversion is actually needed.
Using Version 8.1 of Xcode.
Create an entity named "MapRegionObject" in .xcdatamodeld file.
Using auto code generator, click Editor on the navigation bar -> create NSManagedOject Subclass...
Got two files : MapRegionObject+CoreDataClass.swift and MapRegionObject+CoreDataProperties
Errors in two files showing in the screenshot:
MapRegionObject+CoreDataClass.swift
MapRegionObject+CoreDataProperties
Please help me fix this bugs, thank you so much!
In Xcode 8.1, before using the auto code generator, you must select the entity in your data model:
Then go to the Data Model Inspector tab:
Under "Codegen" select "Manual/Node"
After that you could create a NSManagedObject subclass without errors.
Alternatively, if you have already used 'Class Definition', you can go into your existing .xcdatamodeld file and set all current entities to 'Manual/None' under Codegen. Make sure to save your project (File -> Save), delete your existing Derived Data, clean the project, and then build. Resolved it for me without having to re-make my whole model.
I found this whole thing to be very confusing. You really do need to understand what's new in CoreData. Basically, the default is to automatically generate the class and the extensions for you in a place called "DerivedData" that's buried in ~/Library/Developer/Xcode/DerivedData where these classes and their extensions live, outside of your code source. Personally, not being able to open and look at them is weird to me, but something to get used it.
Basically, if you have an entity called "AppSettings" in your CoreData model, you can just use it without needing to generate the code yourself. If you want the code in your project, then set the Codegen property on the entity to Manual/None. Then do what you did before: Editor->Create NSManagedObject classes, etc. The files will wind up in your project.
The good news is that if you want to make custom extensions, just do that in your project. Xcode will blend the generated files, from their other place outside your project directory, with the files in your project directory.
1) clean the project (cmd + shift + K)
2) In the "data model inspector" for every created Entity set attributes for Class just as in the screenshot below
3) Generate code again (Editor -> create NSManagedObject Subclasses)
After that everything should work fine.
The problem is that you don't need to generate NSManagedObjectModel subclasses manually anymore.
ref: https://forums.developer.apple.com/thread/48988
Xcode automatically generates classes or class extensions for the
entities and properties in a Core Data data model. Automatic code
generation is enabled and disabled on an entity by entity basis, and
is enabled for all entities in new models using the Xcode 8 file
format. This feature is available for any data model that has been
upgraded to the Xcode 8 format. You specify whether Xcode generates
Swift or Objective-C code for a data model using the data model’s file
inspector. When automatic code generation is enabled for an entity,
Xcode creates either a class or class extension for the entity as
specified in the entity's inspector: the specified class name is used
and the sources are placed in the project’s Derived Data. For both
Swift and Objective-C, these classes are directly usable from the
project’s code. For Objective-C, an additional header file is created
for all generated entities in your model: The file name conforms to
the naming convention 'DataModelName+CoreDataModel.h'.
Close the project and follow the following instructions:
Reveal in finder your database .xcdatamodeld file.
on .xcdatamodeld file right click -> Show Package Contents, if(.xcdatamodel) is find again in the package again right click and 'Show Package Contents'. you should get 'contents' file.
Open 'contents' in text edit.
Command-F (codeGenerationType="class") and replace all matchings string with blank string.
Save and open Xcode project again.Everything should work well.
In Xcode 8.2.1 , Menu-Product-Clean,And every is ok, it's so verid.
Don't fight the with Xcode on this unless you really need to alter your generated properties, doing so will just leave you frustrated.
Think of the auto-generated class as any other class in your application. If you need to add functionality to your managed object class, simply change your class definition into an extension and extend your object.
change your class:
class MyManagedObject : NSManagedObject { /* implementation*/ }
to an extension:
extension MyManagedObject { /* implementation */ }
This isn't an answer. It's just explanation of what's going on with the selections
Make sure you see this moment for the Core Data Stanford course
Below is a transcript I wrote myself (It's not 100% accurate):
The default is class definition, if you choose this one. It will
generate that subclass and it will just work. You'll be able to access
your tweets as a class called Tweet. This sounds good. Btw if you do
this it will NOT show up in your file navigator.
The one we choose the most often is the category/extension what this
will do it will only generate an extension of the Tweet class. You
have to actually write the tweet class itself. The extension will take
care of making all the vars. Even when I switch to category/extension
again I don't get that extension showing up in the navigator. It's
kind of hidden from you.
And why do we like this one? Because a lot of times we want to add our
own code. Like in a Tweet, imagine you want to add a static method
that took data from Twitter and turned it into a tweet in the
database. Where would we put code? Well a great place to put that code
would be in the Tweet class...if there was such a thing...and the
extension is going to handle all the var business for you.
If you did choose manual/none for codegen. meaning don't do any
codegen, then you would be doing value/setValue(forKey:)...good luck
with that You're code is going to be a mess. [ie there is no
.propertyName = value ...you'd had to do setValue(value, forKey:
propertyName)].
Long story short, I'm not sure why but for some reason if you don't select the create NSManagedObject subclass then it seems to still work, but without showing what's happening under the hood. Very counter intuitive!
Then also watch this live demo of the Core Data Stanford course:
Now we know we want to access all this stuff not using
value/set(value:forKey:)...we want to have to subclasses of
Users/Tweets. And we want to have vars [ dot notation] for all these
relationships so we need that code to be generate. The way we do that
we just select the entity... and we go down here to CodeGen. This says
by default class definition. That means it's done it. It has generate
a class called Tweet. and It's going to work with var and all
relationships. That's not actually what we want. We want to select
this one [Category/Extension]. Where only create an extension to Tweet
and add the var stuff. That's because we want to write the class
Tweet and put our own code in there. It's very common to write our
own class. But you still want the var magic.
I'm creating a todo app:
I created an entity in my projects .datamodel file, I added the entity an attribute of a string called "content" and created NSManagedObject subclass with the "editor" > "Create NSManagedObject subclass:
This class was created:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface Targets : NSManagedObject
#property (nonatomic, retain) NSString * content;
#end
but now I want another "id" attribute to do I can give each todo an id...
How should I do this? is it ok to do it manually? since I generated it from the entity model it feels like I need to create a new entity to do that...?
If you can direct me, and maybe through an explanation how that works it would be awesome..maybe I dont understand something here and this is why I'm asking this question
In light of the fact that Xcode appears to have a bug which prevents it from appropriately replacing your CoreData files, I've rewritten my answer with a workaround.
Delete the originally generated class file:
(Click move to trash.)
Add the attribute:
Generate the class in the exact manner you generated the original we deleted in step 1:
So, the solution to your problem is actually quite simple:
To add another attribute to your entity, you can just approach it exactly in the same way as you did with the content attribute. Just add the attribute within the graphical editor, just like you did before with content, and then again, use Xcode's built-in functionality to generate the classes, in the same way as you did before.
What will happen is that Xcode overrides the formerly generated files, and replaces them with a new version that now has the attributes. There is no harm in doing that process as often as you like. :)
EDIT:
Short side note, you need to remember to delete the app from your device (or the simulator) after you changed the data model, because otherwise the app will crash because your new Core Data model is not consistent with the one you used before.
Second, if you want to add functionality to your data model classes (which you should do with much care anyway, but maybe some utility Core Data functions or so...), you should make use of iOS categories, so that the code which you wrote for that doesn't get deleted when you regenerate your model classes. The Stanford Lecture on iOS development has a great introduction session on Core Data where this approach is explained in detail!
I'm fairly new to iOS development, so I need some advise.
I'm working on a project (creating a character sheet app for an upcoming table top RPG), and I seem to have put the cart a bit before the horse.
I've already created a subclass of NSObject called characterClass that holds everything I need for a single instance of the character sheet. It also has methods to calculate derived information. I have 2 more classes storing abilities, and have them in NSArrays in the characterClass. Now that it's working, I need to work on data persistance, and storing/retrieving multiple characters.
I'm thinking, of using Core Data, creating a separate entity that matches my characterClass, and having a characterClass init method that pulls the data out of Core Data and puts it into the current instance of characterClass. That seems a bit obtuse to me, (implementing a class separate from the entity) but maybe it's a good way to do it.
I would eventually like to set up dropbox syncing for this information, so whatever I do I would like to have compatible with that. (Core Data with XML files as the backend?)
What would be the best method for something like this?
Rather than just pull the data out of Core Data and use it to create your character class, you could just tweak your character class so that it is an NSManagedObject subclass. That way you are storing your objects directly in Core Data