• 10 hours
  • Medium

Free online content available in this course.

Videos available in this course

Certificate of achievement available at the end this course

Got it!

Last updated on 2/4/19

Implement data components

Log in or subscribe for free to enjoy all this course has to offer!

We’ve got the views set up, and they are ready to accept the functional challenge of the app. Now we need to implement the model.

MVC - Model
MVC - Model

Defining the data model

The model is represented by the objects. To develop the objects we must define the classes that will implement the desired functionality.

Let’s review our main screen:

FrameIT!
FrameIT!

Here we need to accommodate 2 aspects:

  • A color description, by showing the color itself and its caption.  In other words, provide a color swatch.

  • The User's creation, by selecting a particular image and frame color.  

We are going to use a class data type for both components and, to keep it simple, will call those new data classes Creation and ColorSwatch respectively.

To create a data model file, right click on the 'Model' folder in Navigator:

Create new file in Model folder
Create new file in Model folder

Select Swift file and click 'Create':

Choose Swift file
Choose Swift file for model

Name it Creation and click 'Save':

Name your model file
Name your model file

 You'll see the usual swift file contents - a blank canvas for us to paint:

New Swift file ready for our code!
New Swift file ready for our code!

Then, repeat the same steps for ColorSwatch. So, you'll have 2 new files in the Model folder:

Model files
Model files

Now let's dive into the implementation!

Implementing Data Model

Implementing ColorSwatch class

You already know how to create a class, so let's go ahead and start by declaring one:

class ColorSwatch {
    
}

It needs to have 2 properties:

  • Caption - to capture the color's name"Sunshine", etc.

  • Color - to capture the color itself.

Caption is going to be a string and for the color we'll use a new data type:  UIColor . Via its prefix UI, we can guess that it comes from the UIKit framework, so we need to import it. 

import Foundation
import UIKit

class ColorSwatch {
    var caption: String
    var color: UIColor
}

Notice in our code we've got an error stating that our class has no initializers. We declared 2 non-optional properties without assigning values to them. Therefore, we must provide an initializer for it to make sure they have values. Let's create one with parameters for both values:

init(caption: String, color: UIColor) {
    self.caption = caption
    self.color = color
}

That completed our model for color swatch:

import Foundation
import UIKit

class ColorSwatch {
    var caption: String
    var color: UIColor
    
    init(caption: String, color: UIColor) {
        self.caption = caption
        self.color = color
    }
}

Implementing a Creation class

Similarly, we need to declare a Creation class with 2 properties:

  • Image - to capture the chosen image.

  • Color(Swatch) - the chosen swatch for the frame, which is best described with ColorSwatch.

We've got a new data type again -  UIImage  - to store an image:

import Foundation
import UIKit

class Creation {
    var image: UIImage
    var colorSwatch: ColorSwatch
}

Here again, we have 2 non-optional properties that must be initialized. Let's override the initializer without parameters and assign default values to our properties:

static var defaultImage: UIImage {
    return UIImage.init(named: "FrameIT-placeholder")!
}
    
static var defaultColorSwatch: ColorSwatch {
    return ColorSwatch.init(caption: "Simply Yellow", color: .yellow)
}
    
init() {
    // stored property initialization
    image = Creation.defaultImage
    colorSwatch = Creation.defaultColorSwatch
}

Let's review the above:

  • We've created two calculated properties for each of the stored properties: defaultImage  fetches the placeholder image and  defaultColorSwatch  creates an arbitrary value for color swatch.

  • We've created an initializer without parameters which assignes default values from calculated properties to stored properties.

Let's take a closer look at defaultColorSwatch:

image = UIImage.init(named: "FrameIT-placeholder")!

Before a user has selected an image, we can always present a placeholder image that we have provided in our designs and which we have already placed in our image assets. To retrieve a message from the image assets, we use an image object initializer with a parameter that takes the image's name: "FrameIT-placeholder".

Where does the image name come from?

The name is assigned by default using the original file name dragged into the assets catalogue minus the scale suffix - @2x etc.

Back to the image property initialization. You noticed that we have to use  ! . UIImage initializer returns an optional value and we declared our property as non-optional, which means it always has to have a value. This is a reasonable requirement because we will always either have an image picked by a user or we will always assign a placeholder. So, we must unwrap it!

Why is UIImage.init(...) value optional?

The image value returned by image initializer is optional because, to Swift, there's no guarantee that the image by the name we provide exists. We could also write something like this:

image = UIImage.init(named: "imagename-typo")
image = UIImage.init(named: "imaginary-image-we-forgot-to-add")

The examples above are valid from the code syntax perspective. The initializers will attempt to extract images by given names, however they will find nothing in the images assets and will have no other choice but to return nil! That's why we are unwrapping it. We know we've got the image and we can ensure we have typed the name correctly, so it's ok to use unsafe unwrapping - which is what we did with "FrameIT-placeholder".

Improving the model

In our requirements we have stated that once a user has shared their creation, their last color becomes the default for future creations. So, we might as well create a convenience initializer that takes color as a parameter.

At this point we have 2 options for our convenience initializer implementation:

  1. Declare an optional parameter and safely unwrap it within the initializer.

  2. Declare a non-optional parameter, use it without worry, and put all the responsibility of delivering a valid value on the code that will call our initializer. 

Chose your preferred option and try to implement it on your own:

Done yet?
Done yet?

I've chosen to implement a method with optional parameters so that the code that calls my initializer can just throw whatever value at me:

convenience init(colorSwatch: ColorSwatch?) {
    self.init()
    // stored property initialization
    if let userColorSwatch = colorSwatch {
        self.colorSwatch = userColorSwatch
    }
}

As you can see, I safely unwrap the parameter I'm given within my convenience initializer.

But what if the condition is not met and the passed value is nil? :euh:

Even if the color value is nil, we are still good to go because we called the designated initializer we implemented earlier. It took care of assigning default values to all our properties (image and color).

Planning for the future

In our designs we've got a start-over functionality, where our creation needs to be set to a neutral position or in our case, to a placeholder image and the last saved frame color (!). Let's implement a function for that. Since our object Creation won't know anything about the latest saved color swatch, we need to request it as a parameter:

func reset(colorSwatch: ColorSwatch?) {
    image = Creation.defaultImage
    if let userColorSwatch = colorSwatch {
        self.colorSwatch = userColorSwatch
    }
    else {
        self.colorSwatch = Creation.defaultColorSwatch
    }
}

Here's the final code for Creation class:

import Foundation
import UIKit

class Creation {
    var image: UIImage
    var colorSwatch: ColorSwatch
    
    static var defaultImage: UIImage {
        return UIImage.init(named: "FrameIT-placeholder")!
    }
    
    static var defaultColorSwatch: ColorSwatch {
        return ColorSwatch.init(caption: "Simply Yellow", color: .yellow)
    }
    
    init() {
        // stored property initialization
        image = Creation.defaultImage
        colorSwatch = Creation.defaultColorSwatch
    }
    
    convenience init(colorSwatch: ColorSwatch?) {
        self.init()
        // stored property initialization
        if let userColorSwatch = colorSwatch {
            self.colorSwatch = userColorSwatch
        }
    }
    
    func reset(colorSwatch: ColorSwatch?) {
        image = Creation.defaultImage
        if let userColorSwatch = colorSwatch {
            self.colorSwatch = userColorSwatch
        }
        else {
            self.colorSwatch = Creation.defaultColorSwatch
        }
    }
}

Well done! :zorro:

Let's Recap!

  • To define a data model we need to define data types, commonly classes, to describe the requirements.

  • It's recommended to use a separate Swift file to define each class that describes the data model.

  • UIImage class is used to store image objects in the code.

  • An image incorporated in the image assets catalogue can be retrieved by using an image initializer and passing image name.

  • UIColor class is used to store color objects in the code.

Example of certificate of achievement
Example of certificate of achievement