Core Data for iOS. Chapter number 4. Theoretical 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:



    Introduction

    You can create user applications with a stunning interface that helps them in everyday life and solving problems, but if you do not correctly represent the data in the application, the application will become difficult to maintain, its performance will decline and over time it may become completely unusable. In this chapter, we will look at how you need to design models (data) so that they do not spoil your application.

    Database Design

    American philosopher Ralph Waldo Emerson once said: “A foolish consistency is the hobgoblin of little minds” (Stupid adherence to something, this is the lot of fools). People often use this statement in order to protect themselves from attacks and questions from others for details. We hope that we will not fall for this bait, although we periodically claim that Core Data is not a database, but treat it like a database (author: something is remembered “if something walks like a duck, quacks like duck, then it's a duck. ”) In this section, we will consider how to design the application data structure, and drawing a parallel to this process to the process of designing the structure of a relational database will greatly help us not only with the discussion, but also with the final result. At some points, of course, there will be no analogy,

    Entities in Core Data look, act, smell, and taste the same as tables in relational databases. Attributes are the fields of the table, relationships are JOINs by primary and foreign keys, and the entities themselves are the very records in the tables. If our data model is “superimposed” on the SQLite type of storage, then Core Data actually implements everything exactly in the way that we described above and in chapters No. 2 / No. 3. However, do not forget that your data can be stored in memory, or, for example, in some kind of file (atomic storage), and there are no tables, fields, primary keys or foreign ones here. Core Data abstracts the data structure from the type of storage used, thereby achieving convenience and versatility in the design and interaction with data. Let Core Data embrace your mind, merge into one whole with Core Data:

    Beginners who are just starting to study Core Data, creating their first data models, are constantly indignant that they do not have the ability to create and be responsible for primary keys in their entities, as they used to do in traditional modeling of the database structure. There are no auto-incrementing keys in Core Data because they are not needed there. Core Data takes responsibility for creating a unique key for each added object (managed object). No primary keys imply that there are no foreign keys either: Core Data manages the relationships between the entities itself and performs any necessary joins. Stop immediately! Stop feeling uncomfortable with declaring primary and foreign keys!

    Another place that can drag you into a quagmire is a many-to-many relationship simulation. Imagine for a moment that in the League Manager application, players can belong to different teams. If that were the case, then each team could have many players, and each player could play for different teams. If you were to create a traditional relational database, then you would have three tables: one for the teams, the second for the players, and the third for tracking which player and which teams belong. At the same time, in Core Data, they have the same 2 entities - Player and Team, and the many-to-many relationship is configured by setting the usual checkmark. Core Data however, "under the hood" still implements this using 3 tables: ZTEAM, ZPLAYER and Z1TEAMS, but these are implementation details. You don’t need to know that there will be a third or fourth table,

    Tip : Take care of the data, not the storage mechanisms of that data.

    Normalizing a relational database

    The theory of relational database design combines a data modeling process called normalization (normalization process), the goal of which is to reduce or completely eliminate redundancy, as well as provide effective access to data in the database.
    The normalization process consists of five levels, or forms, and work continues at level 6. Only mathematicians will like the definitions of the first five forms, only they will most likely understand them. They look something like this:
    “A relation R is in fourth normal form (4NF) if and only if, wherever there exists an MVD in R, say A -> -> B, then all attributes of R are also functionally dependent on A. In other words, the only dependencies (FDs or MVDs) in R are of the form K -> X (i.e. a functional dependency from a candidate key K to some other attribute X). Equivalently: R is in 4NF if it is in BCNF and all MVD's in R are in fact FDs. ”

    (c) www.databasedesign-resource.com/normal-forms.html

    We have neither enough pages to describe all forms, nor the desire to go through the definitions of all normal forms and explain their essence. Instead, this section will describe each normal form with respect to Core Data and provide some design tips. Remember that following a certain normal form requires following all normal forms that precede a given one.

    A database that corresponds to the first normal form (1NF) is considered normalized. To follow this form (level) of normalization, each record in the database must have the same number of fields. In the context of Core Data, this means that any managed object must have a certain (predefined) number of attributes. Based on the fact that Core Data does not allow you to create one entity with a different number of attributes, we can conclude that the models in Core Data are automatically normalized.

    The second normal form (2NF) and the third normal form (3NF) relate to the relationships between non-key fields and key fields, requiring non-key fields to be facts about the key field to which they belong.Since you don’t care about keys in Core Data, this question is removed. However, you must make sure that all attributes of an entity describe that particular entity, and not something else. For example, the Player entity should not have the uniformColor attribute, since the form color describes the state of the team, rather than the player.

    The following two normal forms, the fourth normal form (4NF) and the fifth normal form (5NF) can be considered the same forms in the Core Data world. They are responsible for reducing or eliminating the redundancy of the described data, pushing you to transfer attributes with multiple values ​​into separate entities and create many-to-many relationships between entities.In terms of Core Data, 4NF, the form states that an entity should not have attributes that can have multiple values. Instead, it’s worth moving these attributes into a separate entity and creating a many-to-many relationship between the entities. Let's look at an example of our application with teams and players from the LeagueManager application. A data model that will violate 4 normal form and 5 normal form will consist of one entity, Team, with an additional attribute - player. Then we will create new Team instances for each player, as a result, we will have several unnecessary (unnecessary) team objects. Instead, in the data model used in the LeagueManager application, a Player entity is created that combines the many-to-many relationship with the Team entity.

    During the modeling process, do not forget about these simple rules. For example, in the data model in the LeagueManager application, the uniformColor attribute in the Team entity represents the ability to normalize. The name of the colors of the forms is a finite set, which means it would be possible to create an additional entity with the name Color, which will have the name attribute and which is connected with the many-to-one relationship with the Team entity.

    Using Model Designer in Xcode

    Some developers simply agree to use the development tools that were provided to them by default. Others create tools only when current ones seem inferior to them. Others stubbornly insist on creating their own tools. We fall into the last category of developers who use only those tools that we ourselves have made. We are proud of the products we create, and sometimes even celebrate small victories and good luck in saving development time. We never consider the time spent on the development of our tools because this time is included in the category of “playing time” for pleasure. However, no matter how surprising it may sound, it never occurred to us to write our own model designer for Core Data, probably because that Xcode is doing a pretty good job. Perfect integration into the development environment, which we probably will not be able to surpass in our tools. In addition, Xcode comes with a built-in graphical data model simulator, which makes it much easier to design. In previous chapters, we already had to deal with it. In this section, we will look at how Core Data works less, and pay attention to the tools that we will often use.

    No code will be written in this section. Instead, we concentrate on the data modeler user interface. To add a data model to our project, select the menu items: File -> New -> New FIle, in the left part of the window select the “Core Data” section. You will see three types of files: Data Model, Mapping Model, NSManagedObject subclass. Mapping models help when migrating data from one data model to another (for more details, see Chapter 8). Select the type of Data Model, specify the name of the data model and save it.


    Opening the newly created model file Xcode opens it in the visual model editor. The presence of a large number of buttons and options can confuse you. The figure below shows the elements that are used in the simulation and their description is given.


    The model editor allows you to easily switch between entities, properties, relationships (relationships), queries, and data model configurations. It is also possible to switch between options for displaying data models: presentation in the form of a graph, or a table view.


    To make it more interesting to discuss the data model, I propose to open the model that we developed in the League Manager application.


    As we can see, by default Xcode displays entities in the form of tables. If it’s more convenient for you to work with the graphical representation of models, you need to click on the “Graph View” button located above the “Editor Style” inscription in the lower right part of the editor.


    The graphic representation should be familiar to the designers and is something standard that describes the entities and the relationships between them. You have probably already noticed that there is a connection from the Team entity to Player with two arrows. This is because a relationship is a one-to-many relationship. A team can have several players. Feedback from Player to Team is a one-to-one type of communication and is accordingly indicated by a single arrow. A player may belong to only one team.
    From the experience of using the visual designer of models, we can confidently say that editing entities and attributes is much more convenient in tabular form, the graphical representation is good for visualizing the relationships between them. However, you may have your own approach.
    You can get more information about a property or entity by selecting him / her. In the figure below, for example, the Player entity is selected. In the upper right part you can see details about the Player entity; her name, her NSManagedObject class, the missing parent class, not an abstract class. When selecting a property, the panel changes its appearance to display the properties of the properties. If we select the email attribute, the panel will change its appearance as follows:


    + The email attribute is called
    + Is optional
    + It is not temporary or indexable
    + Of the String type
    + There is no restriction on the minimum and maximum length
    + There is no default value
    + There are no associated regular expressions
    + Field not indexed by Spotlight
    + The value of the field will not be stored in an external record

    Switching to the team communication of the entity Player changes the panel as follows:


    + The attribute team is called
    + The final (related) entity is the Team entity
    + There is feedback and it is called players
    + It is optional, but not temporary
    + Not is a one-to-many relationship
    + The minimum and maximum number is 1
    + The Nullify delete rule is used
    + The field is not indexed by Spotlight
    + The field value will not be stored in an external record

    Let's look at the Player entity:


    Entities are defined by their names. Core Data allows you to inherit from NSManagedObject in order to provide an alternative implementation of managed objects. If you create a subclass of the NSManagedObject class and want to associate it with a specific entity, then you will need to specify the name of the new class in the "Class" field of the "Entity" section (image above).

    View and edit attributes

    Select any attribute in the “Attributes” section of Xcode so that the right panel changes its presentation and displays all the information for the selected attribute. The table below describes the name of the attribute property and its purpose.
    NameDescription
    NameAttribute name
    TransientSays Core Data does not save this attribute. It is useful to use this type of property in conjunction with the second attribute to support custom or custom types. Custom types will be discussed later in this chapter.
    OptionalAn optional attribute may be nil. Optional attributes must take any non-nil value.
    IndexedIndexed attributes are faster to search, but they take up more storage space.
    Attribute TypeAttribute type. Depending on the selected type, certain validation (verification) fields will be displayed.
    ValidationUsed to set the minimum and maximum length for the selected String type, or the minimum / maximum value for the selected integer type.
    Min lengthCheckbox to enable checking the minimum length value
    Max lengthCheckbox to enable checking the maximum length value
    MinimumCheckbox to enable minimum value check
    MaximumCheckbox to enable maximum value checking
    Default valueThe default value for this attribute if no value was specified.
    Reg. Ex.Regular expression to validate data


    View and edit links

    In the same way as when viewing attribute information, we can view the properties of a link by selecting any link.

    Links have several properties. First, they have a name and a finite entity (entity inwhich is included). The final entity describes the type of object that the link points to. For a Player entity, a team relationship is expected to point to a Team entity. Core Data architects are strongly advised to indicate in each relationship its inverse relashionship, a relationship that is directed in the opposite direction. For example, the Player entity has a relationship with Team, so based on the recommendation, as we have implemented, Team should also have a connection with Player. In fact, if you forget or don’t want to establish feedback, a warning will be generated by the compiler. An additional two properties are available for the attributes: Transient and Optional. A transient link is not stored in storage during a save operation. If the team link was of type Transient, then when you save and then restart the application, information about which team the player belongs to would be lost. The Player object would still be saved, but its association with the Team object is not. Transient communications can be useful when you need to set values ​​at runtime, for example, a password or something that is obtained from information obtained during application operation. If the connection is Optional, then it simply means that you can set it to nil. For example, a player may not have a team. what is obtained from the information obtained while the application is running. If the connection is Optional, then it simply means that you can set it to nil. For example, a player may not have a team. what is obtained from the information obtained while the application is running. If the connection is Optional, then it simply means that you can set it to nil. For example, a player may not have a team.

    The multiplicity of communication determines the number of finite objects to which the original object can relate: to one or to many. By default, the relationship is k-one, which means that the source object can relate to only one final. This is just the case in the example with team to the Player entity: a player can have no more than one team. However, a team can have many players, which is why the player relationship in the Team entity has a many-to-many relationship. These values ​​(from the side of many) are represented as a set of (NSSet) objects. The multiplicity of communication can also be determined by changing the minimum and maximum values, which represent the power of a finite set of entities in the connection.

    In conclusion, Core Data uses the rules for unlinking in order to know what to do with the final objects when deleting the original one. There are many uninstall options that Core Data supports. There are such types of deletions: No action, Nullify, Cascade, Deny. The “Configuration Rules" section in this chapter discusses the removal rules and their meaning.

    Using Queries as Properties

    Until now, we have mentioned attributes and relationships several times in the book. The third property an entity may possess is a query (fetch). Queries as properties are comparable to relationships in that they can refer to other objects. If an entity property of the “Connection” type refers directly to finite objects, then “Requests” (also an entity property) refer to objects selected by the specified predicate. Requests as properties are a kind of “smart” playlists in iTunes, in which the user specifies a music library, and then filters the content according to certain criteria. Although the queries as properties do not behave so much the same as the iTunes selection, they are calculated once at the first request, the results of this selection are cached until we ourselves say that we need to update by calling the methodrefreshObject:mergeChanges:.

    In the League Manager application, we can, for example, create a query property that will return all players from the team whose names begin with "W". Select the Team entity, click the "+" button in the "Fetched Properties" section and name the new request wPlayers. Select the Player entity as the final entity (the type of entity that will be queried and returned).

    In the “Predicate” field, specify the criteria for the filter using the standard NSPredicate format (we will talk more about the format and NSPredicate in Chapter 6). In this example, the predicate is as follows: lastName BEGINSWITH "W".

    After that, the name query we created will become the same attribute of the Player object as the name attribute. You can use it in the same way as other attributes. You can verify that the added request works correctly by adding code to the League Manager delegate method application:didFinishLaunchingWithOptions:, which will use the wPlayer attribute. The code shown below requests all teams, takes the first team (if one exists) and selects all players according to the specified predicate in the wPlayer attribute. After that, all players satisfying the condition are displayed in the console (their names and surnames).

    NSFetchedRequest *fetchRequest = [[NSFetchedRequest alloc] init];
    [fetchRequest setEntity:[NSEntityDescription entityForName:@"Team" inManagedObjectContext:self.managedObjectContext]];
    NSArray *teams = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
    if([teams count] > 0){
       NSManagedObject *team = [teams objectAtIndex:0];
       NSSet *wPlayers = [team valueForKey:@"wPlayers"];
       for(NSManagedObject *wPlayer in wPlayers) {
          NSLog(@"%@ %@", [wPlayer valueForKey:@"firstName"], [wPlayer valueForKey:@"lastName"]);
       }
    }
    

    If we run this code for execution, depending on what data you already have in the application, we should see approximately the following output:
    2011-07-31 19:08:23.005 League Manager[8039:f203] Dwyane Wade 
    2011-07-31 19:08:23.006 League Manager[8039:f203] Tyson Warner
    

    The last important point to consider is creating queries as properties in Xcode. We can use queries as properties in the same way we use NSFetchRequests in an application, with the only difference being that queries as properties are predefined in the model editor. To create a new query as a property that will select all teams with a green form color, for example, select the Team entity in the "Entities" section, hold down the "Add Entity" button until a drop-down list appears, select an option from this list "Add Fetch Request." Call this query “GreenTeams.” In the middle column in Xcode, you should see a “+” button to add a new filter criterion. Click on the "+" button and enter in the text box the line: uniformColor == "Green".


    Requests as properties created in this way are stored in the data model and can be obtained using the fetchRequestTemplateForName:NSManagedObjectModel class method . Add some code to the delegate method application:didFinishLaunchingWithOptions:to verify the health of the newly made request:
    NSFetchRequest *fetchRequest = [self.managedObjectModel fetchRequestForTemplateName:@"GreenTeams"];
    NSArray *teams = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
    for(NSManagedObject *team in teams){
       NSLog(@"%@", [team valueForKey:@"name"]);
    }
    

    Depending on the data that you have in the application, you should see something like the following output:
    2011-07-31 19:17:25.598 League Manager[8184:f203] Boston Celtics
    


    Entity Creation

    So far, we have been very active in discussing entities. Entities describe the attributes and relationships that a managed object has. In order to add a new entity, you must click on the "+" button with the inscription "Add Entity" and specify the name.

    By default, instances of entity objects are represented by the NSManagedObject type, however, as the size and complexity of the project increases, you may need to create your own classes that will represent managed objects. The managed objects you create must be inherited from the NSManagedObject class, the name of the created class will need to be specified in the "Class" field when editing the entity.


    We have not yet touched on one topic at this stage - the inheritance of entities. The entity inheritance mechanism is similar to the class inheritance mechanism when created attributes / methods are inherited. For example, you can create a Person entity, and a Player entity inherit from Person. If the application had other representations of people, for example, a trainer, then the essence of Coach could also be inherited from Person. If we wanted to prevent the programmer from directly creating an instance of the Person entity, then we just need to indicate that this entity is abstract. For example, create a Person entity with the dateOfBirth field and mark it as an abstract entity.


    The next step is to change the Player entity: setting up the parent (inherited) Person entity. Select the Player entity and select “Person” in the drop-down list titled “Parent Entity”:


    Now the dateOfBirth attribute will be available for the Player entity.


    Create Attributes

    Attributes describe an entity. Entity attributes describe the current state of an entity and can be represented by various types of data. Select any entity, in the "Attributes" section click on "+" (adding a new attribute, do not forget to specify a name). The figure below shows the possible attribute settings:


    Earlier in this chapter, we already talked about various attribute properties. Undoubtedly the most important attribute property is its type. By default, Core Data provides several types:



    Most of the types listed above are directly related to data types in Objective-C. The only type that falls out of the picture is the Transformable type, which is designed for custom (created by the programmer) types. It is recommended that you use the Transformable type in cases where none of the existing types can contain the data you need. As an example, we can again take our League Manager application, in which you need to store colors that can be represented by the CGColorRef type. In this case, the attribute must be Transient and Transformable, and at the same time, we will have to introduce the Core Data mechanism for converting the attribute to an existing type. Use custom (custom) attributes when you create your own class, which inherits from NSManagedObject.

    Creating Links

    In the normalization process, you will most likely create several entities, depending on the complexity of the data model. Relationships allow you to associate entities with each other, as is done in the League Manager application with the Player and Team entities. Core Data allows you to tune (I repent, I just like this word) connections so that they, in turn, most accurately reflect the relationship between the simulated data.

    In Xcode, when creating a connection or when choosing one, we see a set of settings that will allow us to change the nature of the connection. For example, if we select the team relationship in the Player entity, we will see approximately the following picture:


    List of fields:
    • Name
    • Ultimate entity
    • Feedback
    • Transient
    • Optional
    • Is the to-many relationship
    • Quantity (minimum and maximum)
    • Removal Rules


    In the following sections, we will examine these fields in more detail, how they affect the properties of the entity and what they mean.

    Name of communication (Name)

    The first field, Name, becomes the name of the attribute NSManagedObject of the object related to the connection. According to the accepted agreements, the name should be in lower case (lowercase). If the connection is “to many,” then the name should take the form of plural. number. It is worthwhile to understand that all these are just agreements, following which will make your models more understandable, readable and easily maintained. You may notice that in the League Manager application we adhere to agreements, therefore the connection in the Player entity is called team. In essence, Team communication with players is called "players."

    Destination and Inverse

    The next field, Destination, defines the entity from the other end of the connection. Inverse field allows you to select the same relationship, but only from the side of the final entity.

    Leaving the Inverse field set to No Inverse Relationship guarantees you receive two warnings: a consistency error and an incorrectly configured attribute.

    You can ignore it, after all, these are just compiler warnings, not errors, but you may encounter unexpected consequences. Core Data uses two-way communication information to maintain object graph consistency to handle the Redo / Undo operation. Leaving the connection without specifying an Inverse entity, you take responsibility for maintaining the consistency of the object graph for performing Undo / Redo operations. Apple documentation strongly advises against this, in particular in the case of using the many-to-many relationship. If you do not specify an Inverse connection, then the managed object from the other end of the connection will not be marked as a changed object, if the managed object from this side of the connection has changed.

    Transient

    By marking the link as “Transient,” you tell Core Data that this link does not need to be stored in storage. Crosslinking still supports Redo / Undo operations, but disappears when the application closes. Here are some possible uses for transitional communication:
    • Relationships between entities that are temporary and should not exist longer than the current application launch session
    • Communication information that can be obtained from some external data sources, be it another Core Data store or some other source of information


    In most cases, you will want your connections to be permanent and maintained.

    Optional

    The next field, Optional, is a checkbox that determines whether the link can be nil or not. Consider it something like NULL or non-NULL values ​​in the database field. If the checkbox is set, then the entity may be saved without specifying a link. If you try to save the entity with the checkbox reset, then the save will fail. If in the Player entity in the League Manager application, leave the team connection with the reset checkbox, then each player will have to belong to some team. Establishing a team connection in nil and further saving (calling the save :) method will result in an error with the text “team is a required value”.

    To-many relationship

    The option is also a checkbox. If the checkbox is set, then the current connection can indicate a lot of objects at that end, if not, then we get an “to-one” connection.

    Quantity (minimum and maximum)

    Defines the number of objects on the communication side that the current object can have. The option is effective only if a “many-to-many” relationship is selected.
    Exceeding the limit while saving will produce an error “Too many items”, and an insufficient number - “Too few items”.
    By the way, the minimum value may be greater than the maximum, which in general will suit Core Data, since verification is not performed. Therefore, if the minimum value is greater than the maximum, any attempt to save the object graph will fail.

    Delete Rule

    Deletion rules determine the actions that Core Data needs to take when deleting an object with links.
    There are 4 types of deletion:
    1. Cascade - the source object is deleted, all related (final) objects are deleted too
    2. Deny - if the source object is associated with some objects, then deletion does not happen
    3. Nullify - the source object is deleted, all associated feedback is set to nil.
    4. No Action - the original object is deleted, related objects do not change in any way

    In the League Manager application, the deletion rule for player communication is Cascade, which means that when a team is deleted, all its players will be deleted as well. If we changed the deletion rule to Deny, the deletion would not have happened if the team had at least one player. Setting the deletion rule to Nullify led to the fact that all players would remain in the repository, but with a null reference to the team, while the team itself would be deleted.

    Only registered users can participate in the survey. Please come in.

    Translation quality

    • 73% 5 57
    • 21.7% 4 17
    • 3.8% 3 3
    • 0% 2 0
    • 1.2% 1 1

    Also popular now: