DPS923 notes – Tue Feb 3

Working more with the Swift language. Introduction to the Core Data technology.

.

Quiz today

We will begin today’s class/session with a quiz.

The quiz will be ten (10) minutes in length.

You will answer three questions, on a single sheet of paper. The questions will cover topics, concepts, definitions, descriptions, and so on. Think about a question-and-answer session in a job interview. Those are the kinds of questions you can expect.

.

Quick Help in Xcode

When writing code, Xcode can display concise reference documentation for an API symbol.

In the Utilities area, show the Quick Help inspector. (Its icon is a circled question mark.)

As you type code, the Quick Help panel will update appropriately.

If you click on an API symbol, it will display its documentation.

If you Option+click on an API symbol, the Quick Help appears in a pop-up window (near the API symbol).

.

Working more with the Swift language

At this point in the course, you have written a notable amount of Swift code.

The code in Lab 1 was simple. The declarations for the outlets were a bit unusual (no initial value was assigned, and the type name had a ! suffix), but we explained that away by saying that “this is how an outlet is declared in code”.

The code in Lab 2 was also simple. It also had similar outlet declarations.

However, there was one unusual bit of code: When reading a segmented control button title, we had to add a ! suffix to the titleForSegmentAtIndex() method. We explained that away by saying that:

The method returns a string value, or nothing. We know it will successfully return a string value, so we add the ! suffix to access the value.

Then, last week, when we began learning about a ‘model’ class, and began work on Lab 3 , the code ended up being littered with ? and ! suffixes, attached to many methods, properties, and types.

The sections below discuss optionals in Swift.

.

Optionals in Swift

When you declare a variable or property, you assign its type, and you often assign a value.

If you don’t know the initial value, or don’t want one, add ? (question mark) as a type suffix. This marks the variable as optional. For example:

// Typical variable
var firstName = “Peter”

// Optional variable
var middleName: String?

Read this section from The Swift Programming Guide before continuing.

Here are its highlights:

You can use an if statement to find out whether an optional contains a value by comparing the optional against nil. You perform this comparison with the “equal to” operator (==) or the “not equal to” operator (!=).

Once you’re sure that the optional does contain a value, you can access its underlying value by adding an exclamation mark (!) to the end of the optional’s name. The exclamation mark effectively says, “I know that this optional definitely has a value; please use it.” This is known as forced unwrapping of the optional’s value.

You use optional binding to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable. Optional binding can be used with if and while statements to check for a value inside an optional, and to extract that value into a constant or variable, as part of a single action. [For example:]

if let myNumber = “1234”.toInt() {

[Within the if statement’s code block, the temporary constant or variable] has already been initialized with the value contained within the optional, and so there is no need to use the ! suffix to access its value.

Sometimes it is clear from a program’s structure that an optional will always have a value, after that value is first set. In these cases, it is useful to remove the need to check and unwrap the optional’s value every time it is accessed, because it can be safely assumed to have a value all of the time.

These kinds of optionals are defined as implicitly unwrapped optionals. You write an implicitly unwrapped optional by placing an exclamation mark (String!) rather than a question mark (String?) after the type that you want to make optional.

You can think of an implicitly unwrapped optional as giving permission for the optional to be unwrapped automatically whenever it is used. Rather than placing an exclamation mark after the optional’s name each time you use it, you place an exclamation mark after the optional’s type when you declare it.

.

Discussion

In the Model class, there is a (suggested) line of code:

let path = NSBundle.mainBundle().URLForResource(“stats2014qb”, withExtension: “plist”)

Look at the Quick Help documentation for the URLForResource(withExtension: ) method. (Click on the method. Then, show the Quick Help inspector in the right side utility area, or Option+click the method to show Quick Help in a popup window.)

What does the method return?

Look at the “Declaration”. It returns ( -> ) an NSURL?, which is an “optional NSURL”.

The “Returns” text explains more: “[Returns the] file URL for the resource file or nil if the file could not be located.”

What does this mean?

Well, we assigned the return value to the “path” constant. The value will be something (the file URL), or nothing (nil). However, we’re pretty sure that the return value will be something (and not nil).

To access the value of the “path” constant, we must add a ! suffix to it: path!

players = NSArray(contentsOfURL: path!)

There are other examples. Look at your source code files, and ask your questions during today’s class/session.

  • App delegate
  • Player list
  • Player detail

.

But wait, there’s more…

When you declare a constant (let) or variable (var) in a class, it is known as a property.

A constant is read-only. A variable is read-write.

If you do not have an initial value for a property, add a ? suffix to the type to make the property an optional.

If the class has an initializer, and the property value is set in the initializer’s code block, add a ! suffix to the type to make it an implicitly unwrapped optional.

Let’s revisit the declaration of outlets…

Outlets are declared and coded by Xcode, when you Ctrl+click-drag-drop a user interface object from the storyboard to the source code of the view/scene’s controller. Notice that each has a ! suffix.

The value will be set by the Cocoa runtime when the scene and controller is initialized.

When you declare a function in a class, it is known as a method.

If its return type could be something, or could be nothing, add a ? suffix to the return type to make it an optional.

.

Downcasting

Read this section in The Swift Programming Guide before continuing.

Here are its highlights:

A constant or variable of a certain class type may actually refer to an instance of a subclass behind the scenes. Where you believe this is the case, you can try to downcast to the subclass type with the type cast operator (as).

Because downcasting can fail, the type cast operator comes in two different forms. The optional form, as?, returns an optional value of the type you are trying to downcast to. The forced form, as, attempts the downcast and force-unwraps the result as a single compound action.

Use the optional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.

Use the forced form of the type cast operator (as) only when you are sure that the downcast will always succeed.

.

Discussion

In the player list (table view) controller, the tableView(cellForRowAtIndexPath: ) method configures and returns a table view cell (to the Cocoa runtime).

Based on the table view row that’s being rendered/drawn, the data source – the model object’s “players” array/collection – is accessed, and a specific object (using its index value) is read/fetched from the data source.

The “players” array/collection type is AnyObject. However, we know that it is a Dictionary<String, AnyObject>.

Therefore, when we get the object from the data source, we downcast the object to the type we want:

let player = model.players[indexPath.row] as Dictionary<String, AnyObject>

Then, for the “player” object, we begin to extract the required key-value pairs:

let playerName = player[“Player”] as String 
let teamName = player[“Team”] as String

The value of “Player” or “Team” is an AnyObject type. However, we know that it is a String.

.

Type Casting for… AnyObject

Read this section in The Swift Programming Guide before continuing.

Here are its highlights:

Swift provides [a] special type [alias] for working with non-specific types:  AnyObject [, which] can represent an instance of any class type.

When working with Cocoa APIs, it is common to receive an array with a type of [AnyObject], or “an array of values of any object type”. This is because Objective-C does not have explicitly typed arrays. However, you can often be confident about the type of objects contained in such an array just from the information you know about the API that provided the array.

In these situations, you can use the forced version of the type cast operator (as) to downcast each item in the array to a more specific class type than AnyObject, without the need for optional unwrapping.

.

Re-writing some of the Lab 3 code

(apply this new knowledge)

.

Classes that describe real-world objects

Before moving on to Core Data, we will briefly discuss how to design and write a custom class that describes a real-world object.

Entity classes use properties for data/state storage, and methods for behaviour. Similar to other languages and frameworks.

In our apps, we work with objects and collections of objects, similar to what you’d do in other languages and frameworks.

What about persistence? How do we persist the object graph? Well, Cocoa offers at least two techniques, archiving, and the Core Data framework, which we study next.

We should note that small and simple object graphs can be persisted in a plist, “property list”. However, this kind of storage is perhaps more suited to, for example, configuration settings, and is usually inadequate for persisting the app’s data.

.

Introduction to Core Data

Core Data is an object design, management, and persistence framework.

It helps you manage the lifecycle of objects and object graphs.

.

How do I get started?

Use a ‘project template’.

In the course GitHub repository, in the Project_Templates folder, you will see a project named ClassesV1.

Make a copy of that, and you will have all you need to get started.

How do I make a copy?

The Readme.txt file in the ClassesV1 project helps you do this.

.

What’s in the “ClassesV1” project template?

The project template has all the pieces you need.

It is nicely organized.

The project template includes the Core Data ‘stack’, which provides the necessary objects, and a factory/builder.

A store initializer is included, enabling you to create startup data for the app when launched for the first time.

Its Model class is configured for Core Data, and has examples of properties of methods.

And it has a table view controller, and a standard view controller, which can use Core Data objects.

.

Where do I see Core Data in a project?

The following screen shot shows some of the Model class in the ClassesV1 project template. (Click to view it in a new tab/window.)

Look at the following notable items:

  • The CDStack class
  • The ObjectModel file, which is the Core Data (object) model created by the model editor
  • Properties and methods in Model.m, shown in the code editor

where-is-cd-in-a-project

.

How do I design my entity objects?

Use the Core Data model editor.

The screen shot below shows an example where two entities were designed. (Click to view it in a new tab/window.)

Look at the following notable items:

  • Add Entity control
  • List of entities that have been designed
  • Add ( + ) and Remove ( – )  properties controls
  • For a selected/highlighted entity, a list of attributes and relationships
  • For a selected/highlighted entity, available settings in the Data Model Inspector (in the right-side Utility area)

how-to-design-entities

.

Naming conventions

Entity names begin with an upper-case letter. Multi-word names use camel-casing.

Property names – attribute, relationship – begin with a lower-case letter. Multi-word names use camel-casing.

Do NOT use “description” for the name of an attribute.

Why?

It’s documented in the Core Data Programming Guide, and in the NSPropertyDescription class reference document. In summary, DO NOT use these names for properties:

  • description
  • class
  • entity
  • objectID
  • self

.

A “to-one” relationship property name is singular. For example, “supplier”.

A “to-many” relationship property name is plural. For example, “products”.

.

After designing an entity, use Xcode to generate a custom class

After designing an entity, and adding and configuring its properties, use Xcode to generate a custom class. Here’s how:

1. Display the Core Data model editor. Select one or more entities.

2. On the Editor menu, choose “Create NSManagedObject Subclass…”. Answer the dialogs appropriately.

generate-custom-class-1

.

generate-custom-class-2

.

generate-custom-class-3

.

The result will be a class that is much more pleasurable to use when writing code.

generate-custom-class-4

.

What is a “managed object”?

“NSManagedObject is a generic class that implements all the basic behavior required of a Core Data model object.” (From the NSManagedObject class reference document.)

NSManagedObject inherits from NSObject.

In addition, we normally will use Xcode to create a custom class for an entity, which inherits from NSManagedObject. For example, if we created a “Person” entity, and generated a custom class, the inheritance hierarchy would look like this:

NSObject > NSManagedObject > Person

.

What is the “managed object context”?

“An instance of NSManagedObjectContext represents a single “object space” or scratch pad in an application. Its primary responsibility is to manage a collection of managed objects. The context is a powerful object with a central role in the life-cycle of managed objects, with responsibilities from life-cycle management … to validation, inverse relationship handling, and undo/redo.” (From the NSManagedObjectContext class reference document.)

Think of it as an in-memory “scratch pad” or temporary “work area” that you use.

.

What ‘management’ tasks can I perform on my objects?

All the tasks you would expect:

Fetch: Get all, or get one, or get some filtered, or get a scalar value (e.g. the number of objects). Done with a “fetch request”, introduced below.

Add: Add new object.

Edit: Edit an existing object.

Remove: Remove an existing object.

These tasks are implemented as methods in the Model class.

.

Where is the data (object graph) persisted?

In your app’s “Documents” directory.

The Core Data stack manages access to the store file. We don’t have to worry about it.

The data format of the store file is private, and is NOT important to us.

.

What is a “fetch request”?

“An instance of NSFetchRequest describes search criteria used to retrieve data from a persistent store.” (From the NSFetchRequest class documentation document.)

What “search criteria“?

  • Name of entity being searched
  • If required, a predicate (logical conditions that constrain a search)
  • If required, sort descriptors

.

What’s a “fetched results controller”?

A wonderful and awesome object.

“You use a fetched results controller to efficiently manage the results returned from a Core Data fetch request to provide data for a UITableView object.” (From the NSFetchedResultsController class reference document.)

Are you planning to use a table view? Then you will want to ‘bind’ it to a fetched results controller. The result? Happiness.

If we have only one entity in our project, we create one fetched results controller.

If we have multiple entities in our project, we create a fetched results controller for each entity.

They are created in our Model class, as properties, with custom getters.

.

How do I…

How do I define an entity object?
Use the Core Data model editor.
Add an entity, and then add and configure properties.
Finally, use Xcode to generate a custom class for the entity.

How do I write code to manage the entity?
Do most of your work in the Model class.
Create a property for the entity’s fetched results controller.
Then write methods for other fetch requests, and for handling object creation, modification, and removal.

How do I perform searches and handle results?
You can use the fetch request object in the fetched results controller if you plan to ‘bind’ the results to a table view.
Alternatively, you can use a fetch request object to do so.
In either case, results are available as an NSArray of zero-or-more objects.

How do I add, edit, and remove objects?
As noted above, write methods that handle object creation, modification, and removal in the Model class.
For ‘add’, create and configure a new object.
For ‘edit’, fetch the object. Then change its property values.
For ‘remove’, fetch the object. Then ask the context to remove it.
Always “save changes”.

.

Can I study a code example?

Yes.

The CD Types code example (in the GitHub repository) is a good example of an app that works with one entity.

You should attempt to re-create this example, using your own copy of the ClassesV1 template. Strongly recommended.

.

Show me a diagram of the objects in an iOS app that uses Core Data

In the style that we have been using in class, here is a diagram. (Click to open it full-size in a new tab/window.)

iOSAppObjectsWithCoreData

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: