converting swift 3 project to use CoreData - swift

I'm relatively new to Swift and Cocoa worlds and would love to get peoples opinions on whats the best way to design the data model for the following:
I currently have a prototype project for macOS that has the following (simplified) data model:
class Loan: NSObject, XMLParseDelegate {
var id: Int
var address: Address
// ... other properties and methods
func parseZillow() {
// bunch of code, etc
}
}
where Address is another class structure that has its own properties and methods. I then have an array of [Loan] bound to NSArrayController and NSTableView using Cocoa Bindings (and everything works nicely, except now i want to introduce persistent storage into the picture)
Now the (two-part) question:
1) if i was to represent this structure with CoreData model - how do I represent the nested class structure of loan.address path? do i just have 2 Entities in CoreData (Loans and Addresses) and have a one-to-one relationship between the two? is this the best practice?
2) my understanding of CoreData is that instead of using the Array of [Loan] as my data model, CoreData will be the new data model (and will create a managed object representing my data schema similar to what i have currently as my class Loan object. Where would I define the methods that I currently have for my Loan and Address classes? Do I need to create a wrapper class on top of the MO? I've looked at a bunch of tutorials but this part is still not clear to me

Yes, you can have two entities, Loan and Address. If each loan has one address and each address has one loan the relationship is one-to-one. If an address can have multiple loans, the relationship is many-to-one.
From Creating and Saving Managed Objects
Creating NSManagedObject Subclasses
By default, Core Data will return NSManagedObject instances to your application. However, it is useful to define subclasses of NSManagedObject for each of the entities in your model. Speciflcally, when you create subclasses of NSManagedObject, you can define the properties that the entity can use for code completion and you can add convenience methods to those subclasses.
To create a subclass of NSManagedObject, in Xcode’s Core Data model editor, select the entity, and in the Entity pane of the Data Model inspector, enter the name in the Class field. Then create the subclass (AAAEmployeeMO) in Xcode.
'Create NSManagedObject Subclass…' is in the Editor menu. You can create all subclasses at once. You can add your methods to the NSManagedObject Subclasses.

Related

Core Data Coding Pattern

I have been working with C# using a repository design pattern with Entity Framework (EF) that allows me to interact with the database using a generic class for each table.
This repository has all the functions that I would need such as: Adding a new entity into the database, updating an existing entity, deleting an entity, saving the context and so on...
Take, for example, I have an entity called 'Person' in the database. I would create a new class called PersonRepository which had all the functions I would need to change/add a value to the database.
As a result, to interact with the database, you create an instance of the repository class. This then allows you to call the functions of this class which in turn interacts with the database. The idea of this pattern is that all your database calls for an entity are isolated into a single class, this improves testability and separation of concerns.
I am learning how to use Core Data in my Swift programs and it appears to be similar to that of EF in C#. I have created the entities in the .xcdatamodel file and created the associated Cocoa Touch class via the Editor > Create NSManageedObject subclasses option.
I used the 'Category/Extension' CodeGen option, so, I then created a new class called 'Exercise'. From what I have read, this class can contain additional logic such as overriding the prepareForDeletion method. Does this mean I can add the addEntity, updateEntity functions onto this method and then call them from other code files?
My questions are:
Can I implement the same repository pattern that I can use in C#?
I believe this would be on the generated class of my entity (see image below)
Where do I place all my database call functions? If I can use this repository pattern I will place them on there, however, if I cannot, do I place them in a separate code file? Should I place them on the generated entity file which is created for the purposes of adding additional logic?
If I can use the repository pattern, is there a way to get the context injected into this class, or, do I have to call a getContext method each time I want to change it? In C# we can use the services to inject the context into the class each time I want to make changes to the database - I am unsure if the same is possible in Swift.
If I understand correctly, in Swift the equivalent of your 'repository' class could be one of two things. It could be context of core data, which you'd run queries and saves to OR (more likely) you would create a new class or struct that obscured away core data from the business side. Assuming the later, your "dataManager" class would perform all interactions with core data. It could then either return NSmanagedobjects to the business side, or you could map/transform/convert to some other type.
Yes, Exercise is a object that subclasses nsmanagedobject, so it has all the core data features of a managed object, but you can add any custom functions your want.
Your data manager class would perform the queries and return the correct objects. For example data manger.getExercises() -> [Exercises], dataManager.save(_ exercise: Exercise), etc....
You'll need to consider thread safety in your pattern. One option is converting your nsmanagedobjects to struct in your datamanager and not returning nsmangedobjects. Structs are thread safe and using codable you can easily convert between exerciseManagedOjbect and exerciseStruct. Plus, with this dataManager class example, you could remove core data for another persistence option in the future with reduced impact to your app overall.

Finding the entities of a CoreData model at runtime

I have an macOS app being developed in Xcode (8.3.2) in SWIFT.
I have a CoreData model with a number of entities and an identifier (myidentifier).
I want to be able to identify the entities of the model programatically at runtime so that I can iterate through the entities and store data being sourced from a range of JSON files based on the data within the JSON file.
To date I have been creating entities and then writing a class for each entity to both save and fetch the data. It is working perfectly and as expected. However, if I continue like this I will end up with about 50 different entities and their associated class files (note that some while entities use a one to many relationship, the majority do not).
I would like to create a single class that will enumerate through the numerous entities of the model and store the relevant data (as well as its associated fetch routine).
I should also note that I am using NSManagedObject subclasses for each of the entities.
How do I obtain a NSManagedObjectModel reference to the model that I am using for the app? I can't seem to find the right mechanism to allow me to do this. Can I do this using the model identifier?
My thinking is that if I can then use entitiesByName I can make use of the resulting [String: NSEntityDescription] to then access my entities and enumerate as needed.
You could use mergedModel(from bundles: [Bundle]?) (see the documentation here, specfying Bundle.main to get the main bundle. Alternatively, your NSManagedObjectContext will have a reference to the persistentStoreCoordinator which will itself have a reference to the managedObjectModel.
Note that NSManagedObjectModel has an entities property so you can use:
for myEntity in myModel.entities { ... }
rather than using entitiesByName.

Do Core Data objects replace standard class objects?

I'm getting into Core Data and have started making "Classes" out of entities. Do I use these classes based off of entities as regular classes with methods etc?
Normally if I was using SQL directly I would have classes for each of my database objects (Tables) that store the working data and perform their functions.
Is this the same with Core Data - do I use the entity-based classes (subclasses of NSManagedObject).
So I might have these entities: Business -> Departments -> Employees
To comply with Core Data - do I make one class for each of these subclassing NSManagedObject and then add methods to it accordingly (like I would an NSObject class)?
Yes, you usually use the Core Data generated classes. You usually don't add methods to those classes, instead you should put them into a Category. This is because if you change an entity, you need to regenerate the class which will erase any methods that you added.
Yes(ish). They are your model classes. You aren't creating them 'out of' entities, they are the code representation of your entities.
You should have one class per entity, but you shouldn't create them yourself. You should get Xcode to create the classes. Or, better yet, use mogenerator.

MVVM viewmodel and model questions

I'm trying to learn MVVM, and i'm struggling a little on differentiating between a model and viewmodel.
If someone could answer these 2 questions it would help clear a lot up for me:
Say I have an Objects class, which is a viewmodel that contains multiple ObservableCollections of Object.
The Object class contains an ObservableCollection of strings that are displayed on the GUI.
Is the Object class a model or viewmodel?
What if the Object class contains just a string and a integer (name and value), is it a model or viewmodel?
The Model is the class that holds your data. The data can be strings /integers or whatever.
The Model can also be a list / collection of those objects. For example a List of Person objects can still be your Model.
The ViewModel is the tier between your Model and the View. It should be used to perform whatever tasks you need on the data (for instance, if your Model is a list of Person objects but you only want to show in your view people that are aged older then 18, this logic is done in the ViewModel)
So to answer your question:
If you have an object which contains the data (in your example a list of strings) it is the Model.
Even if the object is a little more complex (with relation to the number of properties it holds) it's probably still the Model.
Business Logic should be kept separate from the model. On the other hand Validation can be added to the Model (for instance to make sure the Age property of a person is non-negative) since this is still rules on how your data should behave

How to group methods belong to one entity in one class file in Core Data like in Entity Framework?

How to group methods belong to one entity in one class file in Core Data like Entity Framework?
In Linq, we can put all methods in the domain object class and reuse them, in Core Data, is there any way to create different classes for different entities? Or we can only use predicate to retrieve?
It seems that I can't define the class for each entity I configured in the data model. And it is not like hibernate that I can control the physical database schema via ORM. Correct me if I am wrong, I don't believe Core Data can do this as far I know.
So what is the advantage to use Core Data? And is there any mechanism on Cocoa that I can define my domain object classes including primary key, foreign key, for instance, and then create the database schema then?
It seems like the Core Data can only support configuring the objects but there is even no way to configure the physical database via the Core Data.
In your Core Data model you can define which class to instantiate your entities as:
alt text http://img404.imageshack.us/img404/3368/setclass.png
When Core Data fetches objects from the store, it will attempt to create them as instances of this class (although it's not quite that simple, Core Data does some tricks).
You can auto-generate class files for your entities by opening your core data model in a new window, and choosing File->New File:
alt text http://img241.imageshack.us/img241/8238/newfile.png
You should see a new "Managed Object Class" item:
alt text http://img46.imageshack.us/img46/348/managedobjectclass.png
Choose this and select the entities to generate classes for:
alt text http://img241.imageshack.us/img241/5030/selectentity.png
A class will be generated with various properties for getting/setting the attributes on your entity:
alt text http://img10.imageshack.us/img10/9868/generatedproperties.png
If you want to add your own methods, I would recommend you add them to a separate Category. This is because you will often want to regenerate these core data classes when you add new properties or change the entities. If you make additions directly to these files you will lose your changes the next time you generate them.
So create a category called Person+Additions or whatever name you think is appropriate:
alt text http://img294.imageshack.us/img294/8871/addcategory.png
And add any new methods or properties to this class:
alt text http://img717.imageshack.us/img717/3884/addmethods.png
Note that you cannot add new instance variables, but NSManagedObject has facilities to get/set values in an internal dictionary. See the NSManagedObject documentation
So once you've done all this, all you do is cast the NSManagedObject you get from a fetch request (or wherever) to the appropriate type:
#import "Person+Additions.h"
//...
Person *p = (Person *) [fetchResult objectAtIndex:0];
[p myCustomMethod];
//...
As for your other questions about primary keys, and interacting with the physical database, this isn't really what Core Data is designed to provide you. You should not think too much about the underlying database, Core Data is meant to be more high level than that. You define your model, and fetch objects through the API. Primary keys and othe "database-y" details like this are not exposed.
If you want full control over the database you should use the sqlite APIs. But I would say for the large majority of applications Core Data is the better choice.
Look at the docs for KVC ("key-value-coding"). You use key paths to obtain the values you want. You can also sort the data in an array using a key path. For that, take a look at NSSortDescriptors. Otherwise, yes, just change your NSPredicate and re-fetch.