Entities

A relational database has something called a table. A table represents a type: You can have a table of people, a table of a credit card purchases, or a table of real estate listings. Each table has a number of columns to hold pieces of information about the type. A table that represents people might have columns for last name, date of birth, and height. Every row in the table represents an example of the type – e.g., a single person.

This organization translates well to Swift. Every table is like a Swift type. Every column is one of the type’s properties. Every row is an instance of that type. Thus, Core Data’s job is to move data to and from these two representations (Figure 22.1).

Figure 22.1  Role of Core Data

Role of Core Data

Core Data uses different terminology to describe these ideas: A table/type is called an entity, and the columns/properties are called attributes. A Core Data model file is the description of every entity along with its attributes in your application. In Photorama, you are going to describe a Photo entity in a model file and give it attributes like title, remoteURL, and dateTaken.

Modeling entities

The project currently uses an instance of JSONDecoder to map the incoming JSON into model objects for the application to use, and this is still a good idea to avoid manually parsing the JSON. You are about to create a new type to represent a Photo entity, but it will be problematic to have this entity also handle the JSON decoding. It works well to separate the JSON decoding responsibility from the rest of Photo’s responsibilities, so these will be handled by separate types. The existing Photo type will be renamed to FlickrPhoto, and the new entity will be called Photo.

Open Photorama.xcodeproj. In Photo.swift, Control-click the Photo class and select RefactorRename.... Enter FlickrPhoto as the new class name, but do not click Rename yet. While FlickrAPI will use FlickrPhoto, the rest of the app will use a Core Data Photo entity, so you do not want to rename all existing instances of Photo. Within the rename dialog, uncheck the checkboxes for PhotoStore.swift, PhotoDataSource.swift, and PhotoInfoViewController.swift (Figure 22.2). Once you have done that, click Rename to finalize the operation.

Figure 22.2  Renaming the Photo class

Renaming the Photo class

There will be some errors in the project now, since there are references to a Photo type that no longer exists. That is OK; you will fix these errors shortly.

Now create a new file, but do not make it a Swift file like the ones you have created before. Instead, select iOS at the top and scroll down to the Core Data section. Create a new Data Model file (Figure 22.3). Name it Photorama.

Figure 22.3  Creating the model file

Creating the model file

This will create the Photorama.xcdatamodeld file and add it to your project. Select this file from the project navigator and the editor area will reveal the UI for manipulating a Core Data model file.

Find the Add Entity button at the bottom left of the window and click it. A new entity will appear in the list of entities in the lefthand table. Double-click this entity and change its name to Photo (Figure 22.4).

Figure 22.4  Creating the Photo entity

Creating the Photo entity

Now your Photo entity needs attributes. Remember that these will be the properties of the Photo class. The necessary attributes are listed below. For each attribute, click the Creating the Photo entity button in the Attributes section and edit the Attribute and Type values.

  • photoID – a String

  • title – a String

  • dateTaken – a Date

  • remoteURL – a URI

When you are done, your model will look like Figure 22.5.

Figure 22.5  Photo entity with attributes

Photo entity with attributes

Let’s fix some of the errors currently in the project. (If you do not see any errors, you might need to build the project.)

By default, all attributes for a Core Data entity are optional. Core Data uses the term optional to mean something slightly different than Swift optionals. In Core Data, a non-optional attribute must have a value at the time it is saved; it does not need to have a value prior to being saved. Because of this contract, Core Data entity attributes are always represented by Swift optional properties.

This means that while photoID is a String in the Core Data model editor, it is represented by a String? in code. This is the cause of some of the current errors.

Open PhotoStore.swift and navigate to fetchImage(for:completion:). Take a look at the errors you see in this method.

    Value of optional type 'String?' must be unwrapped to a value of type 'String'

To fix this, you will unwrap the optional at the beginning of the method.

At the beginning of fetchImage(for:completion:), unwrap photoKey into a non-optional string. Although this type is optional, in practice it will always have a value.

Listing 22.1  Unwrapping the photoKey (PhotoStore.swift)

func fetchImage(for photo: Photo,
                completion: @escaping (Result<UIImage, Error>) -> Void) {

    guard let photoKey = photo.photoID else {
        preconditionFailure("Photo expected to have a photoID.")
    }

    let request = URLRequest(url: photoURL)

Build the project. While you will still have errors in the project, the errors in fetchImage(for:completion:) should be resolved.

NSManagedObject and subclasses

When an object is fetched with Core Data, its class, by default, is NSManagedObject. NSManagedObject is a subclass of NSObject that knows how to cooperate with the rest of Core Data. An NSManagedObject works a bit like a dictionary: It holds a key-value pair for every property (attribute or relationship) in the entity.

An NSManagedObject is little more than a data container. If you need your model objects to do something in addition to holding data, you must subclass NSManagedObject. Then, in your model file, you specify that this entity is represented by instances of your subclass, not the standard NSManagedObject.

Xcode can generate NSManagedObject subclasses for you based on what you have defined in your Core Data model file.

Open Photorama.xcdatamodeld, select the Photo entity, and open the data model inspector by clicking the right-most icon in the inspector area. Locate the Codegen option and select Manual/None.

With the Photo entity still selected, open Xcode’s Editor menu and select Create NSManagedObject Subclass…. On the screens that follow, make sure the checkbox for Photorama is checked and click Next, then make sure the checkbox for the Photo entity is checked and click Next again. Finally, click Create.

The template will create two files for you: Photo+CoreDataClass.swift and Photo+CoreDataProperties.swift. The template places all the attributes that you defined in the model file into Photo+CoreDataProperties.swift. If you ever change your entity in the model file, you can simply delete Photo+CoreDataProperties.swift and regenerate the NSManagedObject subclass. Xcode will recognize that you already have Photo+CoreDataClass.swift and will only re-create Photo+CoreDataProperties.swift.

Open Photo+CoreDataProperties.swift and take a look at what the template created for you.

All the properties are marked with the @NSManaged keyword. This keyword, which is specific to Core Data, lets the compiler know that the storage and implementation of these properties will be provided at runtime. Because Core Data will create the NSManagedObject instances, you can no longer use a custom initializer, so the properties are declared as variables instead of constants. If you wanted to add computed properties or convenience methods, you would add them to Photo+CoreDataClass.swift, keeping Photo+CoreDataProperties.swift unchanged.

You have created your data model and defined your Photo entity. The next step is to set up the persistent container, which will manage the interactions between the application and Core Data.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset