Good day, good day!
Today I want to start writing a series of lectures with practical tasks on the book “Pro Core Data for iOS” by Michael Privat and Robert Warner, which you can buy from
this link. Each chapter will contain a theoretical and practical part.

Content:
- Chapter number 1. Getting Started ( Practical Part )
- Chapter number 2. Digest Core Data
- Chapter number 3. Data storage: SQLite and other options
- Chapter 4 Creating a data model
- Chapter number 5. We work with data objects
- Chapter number 6. Processing Result Sets
- Chapter number 7. Performance tuning and memory usage
- Chapter number 8. Version Control and Migration
- Chapter number 9. Managing tables using NSFetchedResultsController
- Chapter number 10. Using Core Data in Advanced Applications
')
Introduction
Many developers, after the first acquaintance with Core Data, consider it to be some kind of confusing set of classes, which, instead of simplifying work with data, on the contrary, prevents this. Perhaps this is Rails developers who are used to writing
dynamic search methods and allowing accepted agreements to do all the dirty work for them. Perhaps this is a Java developer who annotates Enterprise JavaBeans (EJB) and
works with Plain Old Java Objects (POJO). No matter who these people are, whatever they do, they don’t perceive the Core Data Framework as it is and its methods of working with data, as many developers see how they create
“live” interfaces using Interface Builder and make fun of them . We assure you that Core Data is not another Ruben Goldberg machine. Classes in the Core Data Framework are rather the players of the 1980s Boston Celtics and, when you understand their approaches to the game, only then can you appreciate the beauty and completeness of their game and interaction.
Did you know?Ruben Lucius Goldberg (born Reuben Lucius Goldberg; 1883–1970) is an American cartoonist, sculptor, writer, engineer and inventor. Goldberg is best known for a series of caricatures featuring the so-called “Ruba Goldberg Machine” - an extremely complex, cumbersome and intricate device that performs very simple functions (for example, a huge machine that occupies an entire room, the purpose of which is to move a spoonful of food from a plate to human mouth). In 1948, Goldberg received the Pulitzer Prize for his political cartoons, and in 1959 the Banshees' Silver Lady Award. Goldberg was one of the founders and the first president of the National Society of Caricature. His name is the Ruben Prize, which the organization rewards as a cartoonist of the year. In the United States annually held a competition cars Ruba Goldberg. |

In this chapter, the purposes of the Core Data Framework classes will be described, both individually and when they work together. Try not to rush to read the entire chapter. Examine the examples, rummage through them, enter the code yourself (
no copy-paste !) And run for verification.
Core Data Classes
In the first chapter, we have already reviewed and created the simplest application using Core Data. You saw code snippets, which classes were used, which methods were called, in what order the methods were called by these methods, and which parameters were passed to these methods. All this was done in order to understand how Core Data works. You blindly followed what you were told to do, maybe at some points you wondered why this was done, but you continued to tap sweet
NSManagedObject
on the keyboard, maybe you asked yourself the question “What happens if you substitute another value here?”. Some may even have tried to replace one value with another, it was they who got something different from the explosion;), and someone got what they expected to receive.
Edward Dijkstra, a well-known computer researcher and winner of the 1972 Turing Award for his work in the development of programming languages, spoke of elegance as the quality that determines victory or defeat, and not as optional luxury.
Did you know?The Turing Award (English Turing Award) - the most prestigious award in computer science, awarded by the Association of computing technology for outstanding scientific and technical contributions in this field.
The award was established by the Computer Engineering Association in honor of the outstanding English scientist Alan Turing, who received the first profound results regarding computability long before the appearance of the first electronic computers.
The prize is awarded annually to one or more specialists in the field of computer science and computing, whose contribution in this area has had a strong and lasting impact on the computer community. The prize may be awarded to one person not more than once. In the field of information technology, the Turing Prize has a status similar to the Nobel Prize in academic sciences. For the first time the Turing Award was awarded in 1966 to Alan Perlis for the development of technology for creating compilers.
The prize is currently sponsored by Intel and Google and amounts to $ 250,000.
(c) Wikipedia
Core Data not only does the task of storing data, but it does it elegantly. To achieve this elegance in your code, you need to understand Core Data, and not guessing in the coffee grounds
how it works and
why it works at all. After reading this chapter, you will understand in detail not only the structure of the Core Data Framework itself, but also how the framework solves complex problems using a small set of classes, making solutions simple, understandable and elegant. Throughout the chapter, we will build and complement the Core Data Framework class diagram. The fact that not all classes will belong to Core Data Framework will not slip away from you, some will be imported from the Foundation Framework. In parallel with the theoretical part, we will develop a small application that will work with fictitious organizational structures (managers, programmers, managers, administrators, etc.).
Open Xcode and create a Single View Application:

Name the project
OrgChart .
Connect to the Core Data project (
see Chapter 1 ). Run the application and make sure that it does not crash.

The image below shows the Core Data classes we usually work with:

In our code, data is saved by adding new
NSManagedObject
to the
NSManagedObjectContext
, and data is obtained using the
NSFetchRequest
class. As shown in Chapter 1, the managed object context is created and initialized using the data warehouse manager (persistent store coordinator) implemented by the
NSPersistentStoreCoordinator
class, which in turn is determined by the data model implemented by the
NSManagedObjectModel
class. The rest of this chapter will be devoted to examining how these classes are created, how they interact with each other, and how to use them.
The classes used to create the model
As previously mentioned in Chapter 1, all applications using Core Data must have an object model of stored data. The model defines the entities and their properties. An entity has three types of properties:
- Attributes
- Relations
- Selection properties
The table you see below shows the various classes and a description of their role.
Going through classes for a better understanding of the model initialization mechanism is quite an interesting exercise, but in practice creating a model in XCode requires you to be able to work with the mouse in the graphical editor of models without writing a single line of code.
Class name | Role |
NSManagedObjectModel | Data model |
NSEntityDescription | Entity in the data model |
NSPropertyDescription | Abstract description of an entity property |
NSAttributeDescription | Entity attribute |
NSRelationshipDescription | Link of one entity to another |
NSFetchedPropertyDescription | Description of a subset of entity instances selected by a specific criterion |
The table below shows the relationships between the classes used in defining the model.
NSManagedObjectContext
does not reference, or refers to several entity objects
NSEntityDescription
. Each
NSEntityDescription
not referenced, or it refers to several
NSPropertyDescription
objects.
NSPropertyDescription
is an abstract class with three concrete implementations:
- NSAttributeDescription
- NSRelationshipDescription
- NSFetchedPropertyDescription

This small number of classes will be enough to describe any data model that you will develop using Core Data Framework. As described earlier in Chapter 1, to create a model, you need to open Xcode, select the menu item File -> New -> New File and specify "Data Model". In this section, we will create a model that will be the organizational structure of the company.
Create a new model in Xcode and name it
OrgChart . The organization has a director (CEO) in this data model. For simplicity, let's imagine that a person has two attributes: a unique employee identifier and a name. Now we are ready to begin defining the data model.
Open the data model and create a new
Organization entity. Like a person, an organization is defined by its unique identifier and name. Add two attributes to the
Organization entity. Attributes are constant properties of an entity that may contain values of some type. Attribute data types are described in the
NSAttributeType
class and each data type imposes some constraints on the entity. For example, if you try to write a string to a property with an integer data type, an error will occur.
The table below describes the existing types:
Attribute type in Xcode | Attribute Type in Objective-C | Objective c | Description |
Integer 16 | NSInteger16AttributeType | NSNumber | 16-bit integer |
Integer 32 | NSInteger32AttributeType | NSNumber | 32-bit integer |
Integer 64 | NSInteger64AttributeType | NSNumber | 64-bit integer |
Decimal | NSDecimalAttributeType | NSDecimalNumber | Base 10 integer value |
Double | NSDoubleAttributeType | NSNumber | Double object wrapper |
Float | NSFloatAttributeType | NSNumber | Float object wrapper |
String | NSStringAttributeType | Nsstring | Character string |
Boolean | NSBooleanAttributeType | Bool | Boolean object wrapper |
Date | NSDateAttributeType | NSDate | date and time |
Binary data | NSBinaryDataAttributeType | NSData | Binary data |
Transformable | NSTransformableAttributeType | Any non-standard type | Any type that can be converted to standard |
NoteIn Chapter 5, we will analyze the type of Transformable in more detail. Transformable attributes are a way to notify Core Data that they will use non-standard data types and, in the process of saving data to the local storage, we will tell you how to convert this type to one of the existing embedded ones.
Let's call the first attribute
id , and the second
name . By default, when creating a new attribute, its type is automatically set to
Undefined and will not allow the project to be compiled. Your task is to choose the appropriate type for each of the entity attributes you create. Organization identifiers will always be of integer type, so we will select
Integer 16 as the type for the
id attribute. Type
String is used for the
name attribute.
At this stage, your project should look like this:

If at this stage the program would be running, the object graph would look like this:

The graph shows that the
NSManagedObjectModel
data model refers to an entity object
NSEntityDescription
, which is called
Organization and uses the
NSManagedObject
type for the property "
managedObjectClassName
. An entity has two attributes. Each attribute contains two properties: name and type. In the first attribute, the name
id
and type
NSInteger16AttributeType
for the properties
name
and
attributeType
respectively.
In the same way, create a second entity named
Person with two attributes:
id and
name . Now we can create a connection between the
Organization and
Person entities and name its
leader . Creating a relationship is quite simple: click on the "+" button in the "Relationships" section, select "Destination" and "Source". Now add another connection between
Person and
Person and call it
employees . The last connection we have created is a one-to-one relationship, while an employee may have several subordinates, which means that the type of communication is necessary one-to-many.
Open the panel on the right in Xcode, as shown:

And tick the box “Plural: To-Many Relationship”:

The data model looks like this:

Here is what the object graph will look like when the model is loaded:

This graph is not a graph of objects that Core Data stores, it is just a display of how the model looks in the eyes of Core Data.
A typical way to load a Core Data data model is as follows:
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"OrgChart" withExtension:@"momd"]; _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
Knowing how model objects are represented in Core Data is not very useful, unless you are writing your own data warehouse or are interested in generating a data model programmatically at runtime. An analogy can be drawn with the creation of a UIView programmatically, instead of using ready-made elements provided by Interface Builder. However, a deep understanding of how Core Data works will allow you to predict and avoid difficulties, problems, and lead to the solution of difficulties in a creative and elegant way.
We familiarized with how entities are represented in Core Data and which auxiliary classes are present in it, but you don’t have to get into the wilds every time you want to use Core Data in your project, rarely when you have to use the previously listed classes directly. All the classes we discussed above are related to the description of the data model. The remainder of this chapter will be devoted to data classes and properties that we will use to access this data.
Data Access Classes
In Chapter 1, we found that the initialization of Core Data begins with loading the data model into an
NSManagedObjectModel
object. In the previous section, we looked at how NSManagedObjectModel represents the internal structure of the data model using
NSPropertyDescription
and
NSEntityDescription
. The next step in the Core Data initialization process is to create and bind the data store using the
NSPersistentStoreCoordinator
. Finally, the final step in the initialization process is the creation of an
NSManagedObjectContext
object with which the interaction takes place directly (reading and writing data).
In order to clarify everything that was said above, I will give a class diagram:

As you can see on the graph there is also
NSManagedObjectModel
, which is loaded and then passed to
NSPersistentStoreCoordinator
to work with data warehouses (yes, there may be several of them) and determine how this data should be processed. The data storage manager (persistent store coordinator) acts as the conductor of the orchestra itself, which coordinates the work of all
NSManagedObjectContext
with the data storage. Among other things, the data warehouse manager is responsible for migrating data from one storage to another, achieved by using the
migratePersistentStore:
method.
NSPersistentStoreCoordinator
initialized with
NSManagedObjectModel
. After our dispatcher has been created, you can upload and register data stores. The code below shows how to initialize the data warehouse manager. Initialization occurs by calling the
initWithManagedObjectModel:
and passing the data model, and then registering the new repository with the
addPersistentStoreWithType:
method.
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"OrgChart.sqlite"]; NSError *error = nil; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; if(![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]){ NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); }
iOS provides three types of storage:
Storage Type | Description |
NSSQLiteStoreType | SQLite database |
NSBinaryStoreType | Binary file |
NSInMemotyStoreType | Data storage in the temporary memory of the device |
NoteCore Data on Mac OS X also offers a fourth storage type ( NSXMLStoreType
), which uses an XML file to store data.
A typical course would be the use of
NSSQLiteStoreType
, the use of which indicates that the data will be stored in a SQLite database. But it is worth all the same to be familiar with other types of storage. No matter how useless the
NSInMemoryStoreType
type
NSInMemoryStoreType
, the typical use of this type of storage is to cache the information received from the remote server and create a local copy of the data in order to reduce the network load and reduce the number of driven traffic along with delays.
After the
NSTersistentStoreCoodrinator
initialized, we can proceed to the creation and initialization of
NSManagedObjectContext
.
The object management environment is responsible for what
is requested from the repository and
what is written there. The task may seem simple, but here are some possible difficulties for you:
- If two threads request the same object, they must receive a pointer to the same object instance.
- If the object is requested frequently, the task of the object management environment is to use the cached object and not to access the repository each time.
- The object management environment must in some way support changes to the repository until a data storage request is made.
NoteApple strongly recommends that you do not inherit your classes from NSManagedObjectContext.
The table below lists the main methods for creating, retrieving, and deleting objects from
NSManagedObjectContext
. Change (obovit) object by changing its properties.
Method name | Method Description |
-executeFetchRequest: error: | Performs a request for receiving objects |
-objectWithID: | Returns an object with the specified ID. |
-insertObject: | Adds a new object to the environment. |
-deleteObject: | Removes an object from the environment. |
It should be careful when deleting objects and the possible consequences of this action. When deleting an object that is associated with others, Core Data needs to decide what to do with the child objects. One of the properties of the relationship is called “Delete rule”, which can be four variants:
Rule Name | Effect |
No action | Does nothing, allows the child to assume that the parent object exists |
Nullify | For each child object sets the reference to the parent object to null. |
Cascade | Deletes each child object. |
Deny | Forbids to delete the parent object, it has at least one child |
In addition to the fact that the object management environment synchronizes data from the repository, it also allows you to perform Undo and Redo operations, as is often done in text editors and other software.
Method | Description |
-undoManager | Returns an NSUndoManager that is responsible for performing undo operations. |
-setUndoManager | Installs a new NSUndoManager |
-undo | Send undo message to NSUndoManager |
-redo | Sends redo message to NSUndoManager |
-reset | Causes the environment to reset all pointers to the objects it processes. |
-rollback | Sends an undo message to the NSUndoManager as long as there are changes. |
-save | Saves all changes to local storage. Consider calling this method a kind of commit in Git. All unsaved data will be lost if the application crashes during execution. |
-hasChanges | Returns YES if there are changes in the environment that were not synchronized with the repository. |
In Chapter 5, we talk about undo and redo operations in Core Data.
Instances of the
NSManagedObject
class support KVC to provide a universal way to access the data contained in an object. The easiest way to access the data of an
NSManagedObject
instance variable is to use the
valueForKey:
method, to write (change) the same data using the
setValue:forKey:
method. The key parameter is the name of the model entity attribute.
- (id)valueForKey:(NSString *)key; - (void)setValue:(id)value forKey:(NSString *)key;
The
NSManagedObject
class also provides several helper methods to help
NSManagedObjectContext
to track object changes. When it comes time to commit to the data warehouse, the
NSManagedObjectContext
all the objects it contains and asks if their state has changed.
Apple strongly discourages overriding methods that are responsible for the state of changes in order not to disrupt the entire chain of actions when saving objects.
NoteEach object of type NSManagedObject contains a link to the environment (NSManagedObjectContext) to which it belongs. This is one of the non-master options to get a link to the environment through the object that is in it.
How to create new NSManagedObject we still remember:
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Organization" inManagedObjectContext:self.managedObjectContext]; NSManagedObject *org = [NSEntityDescription insertEntityForName:[entity name] inManagedObjectContext:self.managedObjectContext];
Once we have created a new object in the environment, we can proceed to setting the values of its fields:
[org setValue:@"MyCompay, Inc." forKey:@"name"]; [org setValue:@77 forKey:@"id"];
KVO
One of the interesting and convenient methods of receiving notification of a change in an object is a “subscription” to events of changes in a property.
NSManagedObject
supports KVO and takes responsibility for sending notifications about changes to a specific property of an object.
KVO is not something that only from the world of Core Data, this pattern is actively used in most Cocoa frameworks. The two main methods that apply to KVO are: willChangeValueForKey:
and didChangeValueForKey:
. The first method is called before the property changes, and the second, respectively, after it.Before you receive notifications, you must register our object (which will track changes) as an observer. Here is what it might look like: [managedObject addObserver:observerObject forKeyPath:@"theProperty" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil];
After the object is registered as a listener, it will receive immediate notifications about changes in the properties for which it is signed.This is an extremely convenient and useful mechanism if you want your screen data to be constantly updated.Query classes
At this stage, we are already familiar with how the Core Data stack is initialized and how to interact with the created objects ( NSManagedObject
). In this part we will talk about how to work with requests for receiving data from the repository.It is not strange, requests for data are instances of the class NSFetchRequest
. The most interesting is the fact that it NSFetchRequest
is almost the only class responsible for requesting (receiving) data in Core Data, the other "auxiliary" classes belong to the Foundation Framework.
Data retrieval requests consist mainly of two parts: an instance NSPredicate
and an instance NSSortDescriptor
. NSPredicate
is designed to filter data according to certain criteria, andNSSortDescriptor
responsible for sorting data in a specific order (ascending, descending). Both of these elements are optional in the query and, if neither of them is specified, you will receive a set of all data in an arbitrary (undefined) order.Let's create a simple request to get a list of all organizations without using filters and sorting: NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Organization" inManagedObjectContext:self.managedObjectContext]; [fetchRequest setEntity:entity]; NSArray *organizations = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
Since we did not filter the list of organizations, we will have all the organizations found in the repository (objects of type NSManagedObject
) in the organizations array .Let's get only those organizations whose names contain the substring "Inc.": NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name contains %@", @"Inc."]; [fetchRequest setPredicate:predicate];
Sort the resulting set of organizations by name in the lexicographical order: NSSortDescriptor *sortByName = [NSSortDescriptor alloc] initWithKey:@"name" ascending:YES]; [fetchRequest setSortDescriptors:@[sortByName]];
In Chapter 6, we will look more closely at working with NSPredicate
and NSSortDescriptor
.Below is the final graph of Core Data objects:
We will discuss NSFetchedResultsController
in more detail in Chapter 9.Class interaction
At this stage, you should have a good idea of the classes that are the core of Core Data and what is happening under the hood. In this part we will continue to consider the OrgChart application, paying attention to the features of the work of certain classes. I want to note that this section (like the previous ones in this chapter) is aimed at developing your basic knowledge about how classes interact in Core Data. Each subsequent chapter will consider concepts (data manipulation, use of several storages of different types, increase in productivity, etc.) at a deeper level.The data model that we built in the previous section should look like this:
For each of the entities, we added the id property(unique identifier) of type Integer 16 . Most programmers, who have probably already come to this point, have already thought about auto-incrementing and may even have tried to look for something similar in XCode. Alas and oh! If you are one of these programmers, you probably already gave up trying to find something like that. And the reason why you could not find it is simple - there is simply no such thing! Core Data manages the object graph using pointers to objects. It makes no sense to use a unique identifier, as is usually done in relational databases like SQLite (yes, maybe Core Data somewhere uses auto-decrementing keys, but we don’t need to worry about this).We intentionally entered a unique identifier in effect for the following reasons:- Raise the issue of auto-increment
- Consider an example of working with integer types.
A personal identifier is something like a social insurance number. There is no need to increment it automatically. As for the essence Organization
, it id
will be a hash of the object, although it does not give a 100% guarantee of uniqueness, but it is suitable for this application.NoteAs a programmer, you should not focus too much on what you know about databases and try to project this knowledge into Core Data. Core Data is not a database, it is a framework for working with an object graph stored locally.
Now we’ll remember a little about the initialization steps for core Core Data objects.OrgChartAppDelegate.h #import <UIKit/UIKit.h> #import <CoreData/CoreData.h> @class OrgChartViewController; @interface OrgChartAppDelegate : UIResponder <UIApplicationDelegate> @property (nonatomic, strong) UIWindow *window; @property (nonatomic, strong) OrgChartViewController *viewController; @property (nonatomic, strong) NSManagedObjectModel *managedObjectModel; @property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator; @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext; - (void)saveContext; - (NSURL *)applicationDocumentsDirectory;
Further, in OrgChartAppDelegate.m we implement 3-4 methods already familiar to us: - (void)saveContext { NSError *error = nil; NSManagedObjectContext *managedObjectContext = self.managedObjectContext; if(nil != managedObjectContext) { if([managedObjectContext hasChanged] && ![managedObjectContext save:&error]){ NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } } } - (NSManagedObjectContext *)managedObjectContext { if(nil != _managedObjectContext) return _managedObjectContext; NSPersistentStoreCoordinator *storeCoordinator = self.persistentStoreCoordinator; if(nil != storeCoordinator){ _managedObjectContext = [[NSManagedObjectContext alloc] init]; [_managedObjectContext setPersistentStoreCoordinator:storeCoordinator]; } return _managedObjectContext; } - (NSManagedObjectModel *)managedObjectModel { if(nil != _managedObjectModel) return _managedObjectModel; NSURL *model = [[NSBundle mainBundle] URLForResource:@"OrgChart" withExtension:@"momd"]; _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:model]; return _managedObjectModel; } - (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if(nil != _persistentStoreCoordinator) return _persistentStoreCoordinator; NSURL *storeURL = [[self applicationDocumentDirectory] URLByAppendingPathComponent:@"OrgChart.sqlite"]; NSError *error = nil; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initManagedObjectMode: self.managedObjectModel]; if(![_persistentStoreCoordinator addStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]){ NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return _persistentStoreCoordinator; } - (NSURL *)applicationDocumentsDirectory { return [[[NSFileManaged defaultManager] URLsForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask] lastObject]; }
In this chapter, we will focus on working with Core Data, rather than on implementing the user interface, so all the work will be done in the method application:didFinishLaunchingWithOptions:
. Then we will learn using external tools to check the operation of Core Data (whether the data is stored in our local storage or not).Before you request any data from the repository, you need to add something there, so change ours application:didFinishLaunchingWithOptions:
as follows: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [self createData]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.viewController = [[OrgChartViewController alloc] initWithNibName:@"OrgChartViewController" bundle:nil]; self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible] return YES; }
In OrgChartAppDelegate.h : - (void)createData;
In OrgChartAppDelegate.m : - (void)createData {
Let's start with creating an organization: NSManagedObject *organization = [NSEntityDescription insertNewObjectForEntityForName:@"Organization" inManagedObjectContext:self.managedObjectContext];
We created an organization, but did not specify its name or identifier. [organization setValue:@"MyCompany, Inc." forKey:@"name"];
As we remember, the id attribute is of type Integer 16. All set values in CoreData must be objects (no int, long, char, enum). We use the wrapper class for the integer type: NSNumber.The code will look like this:
[organization setValue:[NSNumber numberWithInt:[@"MyCompany, Inc." hash]] forKey:@"id"];
Established the name of the organization and a unique identifier. Nothing has been saved to the repository yet, but it NSManagedObjectContext
monitors all changes in the state of objects and stores them in the local repository only after calling the save:
method.An organization without people cannot work, though ...Let's create several employees: NSManagedObject *john = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.managedObjectContext]; [john setValue:@"John" forKey:@"name"]; [john setValue:[NSNumber numberWithInt:[@"John" hash]] forKey:@"id"]; NSManagedObject *jane = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.managedObjectContext]; [jane setValue:@"Jane" forKey:@"name"]; [jane setValue:[NSNumber numberWithInt:[@"Jane" hash]] forKey:@"id"]; NSManagedObject *bill = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.managedObjectContext]; [bill setValue:@"Bill" forKey:@"name"]; [bill setValue:[NSNumber numberWithInt:[@"Bill" hash]] forKey:@"id"];
Now we have an organization and three unrelated people. John is our CEO, and Jane and Billy are helpers.We have two types of communication: one-to-one (the organization has only one CEO) and one-to-many (one employee may have many subordinates).So, linking the organization (making contact) with John and making it a CEO is quite simple: [organization setValue:john forKey:@"leader"];
Core Data perfectly understands what object you have dropped into it and what connection it should be. By the way, if you try to transfer some other type instead of John, the code will not work.Now we need to figure out what to do with Jane and Billy. Core Data returns from a one-to-many relationship a lot in the form of a type NSSet
. Therefore, in order for us to have the opportunity to add new employees, it is necessary to call the method mutableSetValueForKey:
, and not the valueForKey:
one that will return an immutable set of objects.This is what Jane and Billy’s add code would look like: NSMutableSet *johnsEmployees = [john mutableSetValueForKey:@"employees"]; [johnsEmployees addObject:jane]; [johnsEmployees addObject:bill];
All these changes, this whole graph of objects that we created must be saved: [self saveContext];
This completes the implementation of the method createData:
, when you start the application, you will not see anything either in the console or on the simulator screen. In the next section, we will use external tools to check whether it was possible to save data or not.Sqlite
We take a shovel in hand, launch the console and ... let's go: cd ~/Library/Application\ Support/iPhone\ Simulator
Find our database: find . -name "OrgChart.sqlite" –print
It will give us something like this: ./5.0/Applications/E8654A34-8EC4-4EAF-B531-00A032DD5977/Documents/OrgChart.sqlite
Run:
sqlite3 ./5.0/Applications/E8654A34-8EC4-4EAF-B531- 00A032DD5977/Documents/OrgChart.sqlite
At this stage, we have the SQLite shell. We can enter commands. SQLite version 3.7.5 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite>
One of the interesting and useful commands is the command .scheme
that displays the structure of the tables created by Core Data: sqlite> .schema CREATE TABLE ZORGANIZATION ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZID INTEGER, ZLEADER INTEGER, ZNAME VARCHAR ); CREATE TABLE ZPERSON ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZID INTEGER, Z2EMPLOYEES INTEGER, ZNAME VARCHAR ); CREATE TABLE Z_METADATA (Z_VERSION INTEGER PRIMARY KEY, Z_UUID VARCHAR(255), Z_PLIST BLOB); CREATE TABLE Z_PRIMARYKEY (Z_ENT INTEGER PRIMARY KEY, Z_NAME VARCHAR, Z_SUPER INTEGER, Z_MAX INTEGER); CREATE INDEX ZORGANIZATION_ZLEADER_INDEX ON ZORGANIZATION (ZLEADER); CREATE INDEX ZPERSON_Z2EMPLOYEES_INDEX ON ZPERSON (Z2EMPLOYEES); sqlite>
Many internal fields, some additional tables that you can hardly find in the XCode model editor. You can see the field for that automatically incremented key (INTEGER PRIMARY KEY).Let's try to sample all organizations and employees available in the database: sqlite> select Z_PK, ZID, ZLEADER, ZNAME from ZORGANIZATION; 1|-19904|2|MyCompany, Inc. sqlite> select Z_PK, ZID, Z2EMPLOYEES, ZNAME from ZPERSON; 1|6050|2|Jane 2|-28989||John 3|28151|2|Bill sqlite>
Note the 3 column in which the identifier of the parent object is indicated, in this case we have Billy and Jane under the direction of John (the unique identifier is 2).To exit the shell: sqlite> .quit
We are requesting data
In this chapter, we wrote data to the SQLite repository using Core Data, and used the SQLite shell to examine the structure of the database that Core Data creates and verify that the data was saved.Now we will request data from the repository using the same Core Data. Open the Xcode file OrgChartAppDelegate.m
.Since the relationship between employees is recursive (one entity on both sides of the relationship), we write a method that allows you to display a list of employees who are subject to the specified c pleasant and understandable formatting: - (void)displayPerson:(NSManagedObject *)person withIndentation:(NSString *)indentation { NSLog(@"%@Name: %@", indentation, [person valueForKey:@"name"]);
Now we will write the method of sampling all organizations, displaying the name of the organization with its CEO and all subordinates: - (void)readData { NSManagedObjectContext *context = [self managedObjectContext]; NSEntityDescription *orgEntity = [NSEntityDescription entityForName:@"Organization" inManagedObjectContext:context]; NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; [fetchRequest setEntity:orgEntity]; NSArray *organizations = [context executeFetchRequest:request error:nil]; id organization; NSEnumerator *it = [organizations objectEnumerator]; while ((organization = [it nextObject]) != nil) { NSLog(@"Organization: %@", [organization valueForKey:@"name"]); NSManagedObject *ceo = [ogranization valueForKey:@"leader"]; [self displayPerson:ceo withIndentation:@" "]; } }
Do not forget to add methods to OrgChartAppDelegate.h
: - (void)readData; - (void)displayPerson:(NSManagedObject *)person withIndentation:(NSString *)indentation;
Now back to the method application:didFinishLaunchingWithOptions:
and instead of calling the method, createData
call readData
: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
Start the application and take a look at the console:
Don't be afraid to experiment: add new employees, delete the database and let the application restore it again, create new organizations.Here, for example, we added a new employee, headed by Jane:
Conclusion
To date, Core Data should not look like something incomprehensible and scary to you. In this chapter, we explored many of the classes that Core Data uses for its needs, but with which we will very, very rarely (or rather, never) work. Using Core Data is like driving a car without having to be a mechanic. The simplicity of the core data class structure should not be misleading. You have already seen what Core Data is capable of and, this is just the beginning. The more we delve into the many settings, connectivity with the UI , alternative storage types, customizable NSManagedObject
's, the clearer you will be able to see the elegance of the applied approaches to data storage in Core Data.