Core Data for iOS. Chapter number 2. Practical part

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



    Content:
    • Chapter number 1. Getting started ( practical part )
    • Chapter number 2. Master Core Data ( Practical Part )
    • Chapter number 3. Data storage: SQLite and other options
    • Chapter number 4. Creating a data model
    • Chapter No. 5. Working with data objects
    • Chapter number 6. Processing Result Sets
    • Chapter number 7. Performance and memory tuning
    • Chapter number 8. Versioning and Migration
    • Chapter number 9. Manage tables using NSFetchedResultsController
    • Chapter number 10. Using Core Data in Advanced Applications



    Introduction

    We will create such a model:
    image

    Then we will add some records and request them. The output will be made to the console so that we do not even ask visual questions.
    Ready? Then let's go!

    Description

    We will create an object graph of our favorite resource - Habr.
    There are N main objects:
    • Blog post
    • User
    • Tags
    • Hubs
    • Questions
    • The answers
    • Company account

    This will be enough.

    What data does each of the objects contain?
    • Blog Entry - Title, Text
    • User - nickname, karma, rating, gender, avatar, mailbox
    • Tag - Name
    • Hub - name, profile whether the hub
    • Question - title, text
    • The answer is text
    • Company account - name of organization, rating


    Getting to work

    Open Xcode and create a new project Single View Application:


    Enter the name of the project, prefixes and more:
    image

    A familiar window:
    image

    Add Core Data

    Add the Core Data Framework to the project:
    image

    Create models

    Add a new file -> iOS -> CoreData -> Data Model
    image
    image

    Let's start with a blog post.
    Create a new entity and call it Blogpost , add two attributes caption (String) and text (String).
    image

    Create a new entity and call it User , add the attributes avatar (String), email (String), gender (Decimal), karma (Integer 16), nickname (String), rating (Integer 16).
    image

    Create a new entity and call it Tag , add only one attribute name (String).
    image

    Create a new entity and name it Hab , add two attributes name (String), target (Boolean).
    image

    Create a new entity and call it Question , add two attributes caption (String), text (String).
    image

    Create a new entity and call it Response , add one attribute text (String).
    image

    We create a new entity and call it Organization , add two attributes name (String), rating (Integer 16).
    image

    Here is what we have as a result:
    image

    Now we are going to establish relations between objects.
    A company account has many users (employees).
    The user has many blog entries, the blog entry has many tags and hubs.
    The user has many questions, and the question has many answers.

    Let's start with building the “company account” (one-to-many) “users” relationship.
    We select Organization from the list of entities and add a new relationship by clicking on the "+" in the Relationships section:
    image

    Since by default Xcode creates a one-to-one relationship, we must change the type of connection:
    image

    Now each organization has many users. We will also set the Inverse field (feedback) now, but first add a new connection to the User entity and name itorganization (the organization in which the user works, and if he doesn’t work anywhere, the field will be null):
    image

    Now, open the editing of the Organization entity again and set the Inverse field in organization :
    image

    Here’s what the object graph now looks like:
    image

    Try to arrange the rest of the connections yourself, and then check with what I did.
    You know how to create a connection, you know how to change the type of connection from one-to-one to one-to-many - this will be enough.
    The final picture:
    image

    Create an organization

    We already have in AppDelegate.h this:
    //
    //  TMAppDelegate.h
    //  Habrahabr
    //
    //  Created by AndrewShmig on 8/31/13.
    //  Copyright (c) 2013 TM. All rights reserved.
    //
    #import 
    #import 
    @class TMViewController;
    @interface TMAppDelegate : UIResponder 
    @property (strong, nonatomic) UIWindow *window;
    @property (strong, nonatomic) TMViewController *viewController;
    @property (nonatomic, strong) NSManagedObjectModel *managedObjectModel;
    @property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;
    @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
    @end
    

    And in AppDelegate.m:
    //
    //  TMAppDelegate.m
    //  Habrahabr
    //
    //  Created by AndrewShmig on 8/31/13.
    //  Copyright (c) 2013 TM. All rights reserved.
    //
    #import "TMAppDelegate.h"
    @implementation TMAppDelegate
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        return YES;
    }
    #pragma mark - Core Data Stack
    - (NSManagedObjectModel *)managedObjectModel
    {
        if(nil != _managedObjectModel)
            return _managedObjectModel;
        _managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
        return _managedObjectModel;
    }
    - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
    {
        if(nil != _persistentStoreCoordinator)
            return _persistentStoreCoordinator;
        NSURL *storeURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
                                                                   inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:@"Habrahabr.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();
        }
        return _persistentStoreCoordinator;
    }
    - (NSManagedObjectContext *)managedObjectContext
    {
        if(nil != _managedObjectContext)
            return _managedObjectContext;
        NSPersistentStoreCoordinator *store = self.persistentStoreCoordinator;
        if(nil != store){
            _managedObjectContext = [[NSManagedObjectContext alloc] init];
            [_managedObjectContext setPersistentStoreCoordinator:store];
        }
        return _managedObjectContext;
    }
    @end
    

    We rewrite the application: didFinishLaunchingWithOptions: method in this way:
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    //    Создаем организацию
        NSManagedObject *yandex = [NSEntityDescription insertNewObjectForEntityForName:@"Organization"
                                                                inManagedObjectContext:self.managedObjectContext];
        [yandex setValue:@"Yandex Inc." forKey:@"name"];
        [yandex setValue:@672 forKey:@"rating"];
    //    создаем сотрудников Yandex
        NSManagedObject *pupkin = [NSEntityDescription insertNewObjectForEntityForName:@"User"
                                                                inManagedObjectContext:self.managedObjectContext];
        [pupkin setValue:@"VaseaPup" forKey:@"nickname"];
        [pupkin setValue:@"vasilisa@yandex.ru" forKey:@"email"];
        [pupkin setValue:@1 forKey:@"gender"]; // 0 - unknown, 1 - male, 2 - female
        [pupkin setValue:@0 forKey:@"karma"];
        [pupkin setValue:@0 forKey:@"rating"];
        NSManagedObject *gosha = [NSEntityDescription insertNewObjectForEntityForName:@"User"
                                                               inManagedObjectContext:self.managedObjectContext];
        [gosha setValue:@"Goshka" forKey:@"nickname"];
        [gosha setValue:@"gosha.k@yandex.ru" forKey:@"email"];
        [gosha setValue:@0 forKey:@"gender"];
        [gosha setValue:@0 forKey:@"karma"];
        [gosha setValue:@0 forKey:@"rating"];
    //    связываем сотрудников с компанией
        NSMutableSet *employees = [yandex mutableSetValueForKey:@"employees"];
        [employees addObject:pupkin];
        [employees addObject:gosha];
    //    сохраняем данные в хранилище
        [self saveContext];
        NSLog(@"Finish!");
        return YES;
    }
    

    Launch the application. Data should have been saved. Check this out.
    Find the Habrahabr.sqlite file:
    image

    Run the terminal and check the database structure:
    AndrewShmigs-MacBook-Pro:~ new$ cd "/Users/new/Library/Application Support/iPhone Simulator/6.1/Applications/95B0716A-9C2C-4BD8-8117-62FB46BB5879"
    AndrewShmigs-MacBook-Pro:95B0716A-9C2C-4BD8-8117-62FB46BB5879 new$ ls
    Documents	Habrahabr.app	Library		tmp
    AndrewShmigs-MacBook-Pro:95B0716A-9C2C-4BD8-8117-62FB46BB5879 new$ cd Documents/
    AndrewShmigs-MacBook-Pro:Documents new$ ls
    Habrahabr.sqlite
    AndrewShmigs-MacBook-Pro:Documents new$ sqlite3 Habrahabr.sqlite 
    SQLite version 3.7.12 2012-04-03 19:43:07
    Enter ".help" for instructions
    Enter SQL statements terminated with a ";"
    sqlite> .schema
    CREATE TABLE ZBLOGPOST ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZAUTHOR INTEGER, ZCAPTION VARCHAR, ZTEXT VARCHAR );
    CREATE TABLE ZHAB ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZTARGET INTEGER, ZBLOGPOSTS INTEGER, ZNAME VARCHAR );
    CREATE TABLE ZORGANIZATION ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZRATING INTEGER, ZNAME VARCHAR );
    CREATE TABLE ZQUESTION ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZAUTHOR INTEGER, ZCAPTION VARCHAR, ZTEXT VARCHAR );
    CREATE TABLE ZRESPONSE ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZQUESTION INTEGER, ZTEXT VARCHAR );
    CREATE TABLE ZTAG ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZBLOGPOST INTEGER, ZNAME VARCHAR );
    CREATE TABLE ZUSER ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZKARMA INTEGER, ZRATING INTEGER, ZORGANIZATION INTEGER, ZGENDER DECIMAL, ZAVATAR VARCHAR, ZEMAIL VARCHAR, ZNICKNAME 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 ZBLOGPOST_ZAUTHOR_INDEX ON ZBLOGPOST (ZAUTHOR);
    CREATE INDEX ZHAB_ZBLOGPOSTS_INDEX ON ZHAB (ZBLOGPOSTS);
    CREATE INDEX ZQUESTION_ZAUTHOR_INDEX ON ZQUESTION (ZAUTHOR);
    CREATE INDEX ZRESPONSE_ZQUESTION_INDEX ON ZRESPONSE (ZQUESTION);
    CREATE INDEX ZTAG_ZBLOGPOST_INDEX ON ZTAG (ZBLOGPOST);
    CREATE INDEX ZUSER_ZORGANIZATION_INDEX ON ZUSER (ZORGANIZATION);
    sqlite> select * from ZORGANIZATION;
    1|3|1|672|Yandex Inc.
    sqlite> select * from ZUSER;
    1|7|1|0|0|1|0||gosha.k@yandex.ru|Goshka
    2|7|1|0|0|1|1||vasilisa@yandex.ru|VaseaPup
    sqlite> 
    

    Now add a question to one of the employees and create some kind of post.
    //    добавляем сотруднику Гоше вопрос
        NSManagedObject *whoAmI = [NSEntityDescription insertNewObjectForEntityForName:@"Question"
                                                                inManagedObjectContext:self.managedObjectContext];
        [whoAmI setValue:@"Who am I?" forKey:@"caption"];
        [whoAmI setValue:@"Помогите мне узнать себя лучше." forKey:@"text"];
        NSMutableSet *goshasQuestions = [gosha mutableSetValueForKey:@"questions"];
        [goshasQuestions addObject:whoAmI];
    

    Run the application and check the database:
    sqlite> select * from ZQUESTION;
    1|4|1|4|Who am I?|Помогите мне узнать себя лучше.
    sqlite> 
    

    Add a blog post to employee Vasya Pupkin:
    //    добавляем сотруднику Васе Пупкину новый блогпост
        NSManagedObject *newPost = [NSEntityDescription insertNewObjectForEntityForName:@"Blogpost"
                                                                 inManagedObjectContext:self.managedObjectContext];
        [newPost setValue:@"yandex.Деньги & yandex.Карты & yandex.ДваСтвола" forKey:@"caption"];
        [newPost setValue:@"Some text" forKey:@"text"];
    //    добавляем два Хаба
        NSManagedObject *hab1 = [NSEntityDescription insertNewObjectForEntityForName:@"Hab"
                                                              inManagedObjectContext:self.managedObjectContext];
        [hab1 setValue:@"iOS" forKey:@"name"];
        [hab1 setValue:@YES forKey:@"target"];
        NSManagedObject *hab2 = [NSEntityDescription insertNewObjectForEntityForName:@"Hab"
                                                              inManagedObjectContext:self.managedObjectContext];
        [hab2 setValue:@"Objective-C" forKey:@"name"];
        [hab2 setValue:@YES forKey:@"target"];
        NSMutableSet *habs = [newPost mutableSetValueForKey:@"habs"];
        [habs addObject:hab1];
        [habs addObject:hab2];
        [[pupkin mutableSetValueForKey:@"blogposts"] addObject:newPost];
    

    And the conclusion:
    sqlite> select * from ZBLOGPOST;
    1|1|1|5|yandex.Деньги & yandex.Карты & yandex.ДваСтвола|Some text
    sqlite> select * from ZHAB;
    1|2|1|1|1|iOS
    2|2|1|1|1|Objective-C
    sqlite> 
    


    Data output

    List all employees and company names in which they work. We will write the code for reading data in the same method in which we wrote the data record.
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"User"];
        NSArray *allUsers = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
        for(NSManagedObject *user in allUsers){
            NSString *nickname = [user valueForKey:@"nickname"];
            NSString *organization = [user valueForKeyPath:@"organization.name"];
            NSLog(@"%@ works at %@", nickname, organization);
        }
    

    We get approximately the following output (my application was launched several times and the data was also added several times):
    2013-08-31 13:00:27.255 Habrahabr[18148:c07] Goshka works at Yandex Inc.
    2013-08-31 13:00:27.257 Habrahabr[18148:c07] VaseaPup works at Yandex Inc.
    2013-08-31 13:00:27.258 Habrahabr[18148:c07] VaseaPup works at Yandex Inc.
    2013-08-31 13:00:27.258 Habrahabr[18148:c07] Goshka works at Yandex Inc.
    2013-08-31 13:00:27.259 Habrahabr[18148:c07] VaseaPup works at Yandex Inc.
    2013-08-31 13:00:27.259 Habrahabr[18148:c07] Goshka works at Yandex Inc.
    2013-08-31 13:00:27.260 Habrahabr[18148:c07] Finish!
    

    Pay attention to how we got the name of the organization in which the user works. How do you like it? A? I thought I would like it!

    In conclusion

    Experiment! Do not be afraid if something breaks, the full project can be found at this link.

    Good luck and thank you for your attention!
    I hope you enjoyed the practical part.

    Also popular now: