• 20 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 3/6/20

Design managing controllers

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

As we always aim to implement the best code, letโ€™s define some managing controllers in our application to lay a SOLID foundation for our code architecture!

What exactly do we need to accomplish?

We'll use two helper elements:

  • Data manager - to manage data components. We'll name it just that: DataManager . ๐Ÿ˜‰

  • Network manager - to help us with networking and connecting the external resources, as well as serving the content to the rest of the app. We'll be a bit more creative here and name it  MediaService for a few of reasons:

    • NetworkManager  would imply a more general layer of networking. It's usually present in larger projects and often responsible for managing related services

    • Delivering data over the network is also typically associated with serving; therefore, using the  Service suffix for the part that transfers the data is more suitable.

    • Finally, it's cleaner to separate portions of data delivery for each data type (or even subtypes if the more general object is too large). In our case, it could be one serving media and another serving swag components. ๐Ÿ˜‰

What about  DataManager ? Isn't that one too general?

DataManager  is a generic name, and in larger projects, it would be dedicated to managing other managers. In this course, we'll use it to manage both data elements - media and swag.

Let's now create the two new management files:

  • DataManager.swift

  • MediaService.swift

Wait, wouldn't that be a job for controllers? ๐Ÿค”

Exactly, it is! We could even keep the keyword  controller  in the name - DataController  and  MediaController  - but we want to avoid confusing them with view controllers and the files named  ViewControllers ! Using too much  controller  code is what earns MVC architecture a bad reputation. So, let's not do that! ๐Ÿ˜…

Data manager

In this example, the data manager will be responsible for holding the data and providing it when needed for the UI of the app.

At this moment, we will also create sample data to represent the media list and some made-up data to emulate the swag:

import Foundation
class DataManager {
static let shared = DataManager()
private init() {
}
lazy var mediaList: [MediaBrief] = {
var list = [MediaBrief]()
for i in 0 ..< 10 {
let media = MediaBrief(id: 486040195,
title: "fakeTitle \(i)",
artistName: "fake artist",
artworkUrl: "https://is4-ssl.mzstatic.com/image/thumb/Music/v4/33/ed/8e/33ed8eb0-4768-c14a-7e21-c421b9647e09/source/100x100bb.jpg")
list.append(media)
}
return list
} ()
func swagForMedia(_ media: Media?) -> Swag? {
let swag = Swag(id: 0,
title: "Cool stuff!",
artworkUrl: "https://is4-ssl.mzstatic.com/image/thumb/Music/v4/33/ed/8e/33ed8eb0-4768-c14a-7e21-c421b9647e09/source/100x100bb.jpg",
sourceUrl: "http://openclassrooms.com")
return swag
}
}

Let's look at the above - there are a number of new things to notice:

  • static let shared = DataManager()  - the class declares a static variable that's initialized with an instance of that class itself! ๐Ÿ˜ฑ

  • private init() { ... } - class initializer declared as private - means no other objects can initialize it! ๐Ÿ˜ฑ

  • lazy var mediaList: [Media] = { ... } ()  - a variable that's declared as lazy and is initialized as a closure! ๐Ÿ˜ฑ

All that is pretty unusual. Let's look at it more closely.

First we'll cover the first two at the same time - our class implements a singleton code design pattern.

Singleton code design pattern

There are various ways to share data and objects between views. One of them is using singleton code design pattern. It allows us to use a single instance of an object throughout the whole application. There are a few precautions against using this pattern; the major one is that this instance can be changed by any piece of code that has access to it. Hence, its state can be unpredictable. We need to take care of that.

Just like MVC architecture, it can be unnecessarily overused and abused.

Back to our code.

Singleton is implemented by declaring a static property of a class that returns an instance of that class. Since it's static, it guarantees that only one property per class will exist. Since the only initializer is declared as private, that property is the only place where we create an instance of that class.

Hence, we get a single object of a class to be used in the entire app! 

We're using it to store a list of media items we get from the internet.

That brings us to that very property -  mediaList - which we marked as  lazy .

Lazy variables

Lazy  properties are those that "pretend" to be initialized at declaration, however, get actually initialized on the first request.

Why is this useful? ๐Ÿค”

This approach saves resources and is used for rather "heavy" values.

Let me make an exaggerated example. Imagine an array for hundreds of elements with each instantiating another heavy object of some class:

class InnocentClass {
var intenseContainer = [
HeavyClass0(),
HeavyClass1(),
...
HeavyClass999(),
]
}

In this example, when we create an instance of that class:

let innocentInstance = InnocentClass()

That array gets initialized with all the elements right away, even though we may not need its property intenseContainer , or will use it in some conditions later in the code.

By using  lazy,   intenseContainer  property will be initialized when used the first time:

class InnocentClass {
lazy var intenseContainer: [Any] = {
return [
HeavyClass0(),
HeavyClass1(),
...
HeavyClass999(),
]
} ()
}
let innocentInstance = InnocentClass()
let heavyValue = innocentInstance.intenseContainer

To declare a  lazy  property, we must specify its type, just like we would declaring a variable without assigning a value to it. We then assign value to it provided by a code block - closure - that will be executed when the variable Is accessed for the first time.

Now the actual array gets composed only when we call it: let heavyValue = innocentInstance.intenseContainer .

And, if we don't ever need it in code, our  innocentInstance  will remain light! ๐Ÿ˜Ž

Media service

This component will be responsible for external communications - with iTunes specifically.

In essence, we need it to provide two elements:

  • A list of media items to display on a grid 

  • A single media element based on a user's selection with more details

Let's create placeholders for this:

class MediaService {
static func getMediaList() {
}
static func getMedia() {
}
}

This class doesn't need to be implemented as singleton.

We'll implement the particulars of this class in the following chapters of this course. ๐Ÿ˜‰

Let's recap!

  • To manage most of the data-related components, it's best to create dedicated managing code (additional managers/services/controllers) and keep them separate from view controllers.

  • Singleton is a code design pattern that allows us to have only one instance of an object throughout the whole app.

  • The singleton pattern must be used carefully as it may be modified by parts of the code making it unpredictable for other parts of the code.

  • Lazy  variables are used to implement initialization on demand instead of at declaration

Example of certificate of achievement
Example of certificate of achievement