Chapters

Hide chapters

Design Patterns by Tutorials

Third Edition · iOS 13 · Swift 5 · Xcode 11

6. Singleton Pattern
Written by Joshua Greene

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

The singleton pattern restricts a class to only one instance. Every reference to the class refers to the same underlying instance. This pattern is extremely common in iOS app development, as Apple makes extensive use of it.

The “singleton plus” pattern is also common, which provides a shared singleton instance that allows other instances to be created, too.

When should you use it?

Use the singleton pattern when having more than one instance of a class would cause problems, or when it just wouldn’t be logical.

Use the singleton plus pattern if a shared instance is useful most of the time, but you also want to allow custom instances to be created. An example of this is FileManager, which handles everything to do with filesystem access. There is a “default” instance which is a singleton, or you can create your own. You would usually create your own if you’re using it on a background thread.

Playground example

Open FundamentalDesignPatterns.xcworkspace in the Starter directory and then open the Overview page.

import UIKit

// MARK: - Singleton
let app = UIApplication.shared
// let app2 = UIApplication()
public class MySingleton {
  // 1
  static let shared = MySingleton()
  // 2
  private init() { }
}
// 3
let mySingleton = MySingleton.shared
// 4
// let mySingleton2 = MySingleton()
// MARK: - Singleton Plus
let defaultFileManager = FileManager.default
let customFileManager = FileManager()
public class MySingletonPlus {
  // 1
  static let shared = MySingletonPlus()
  // 2
  public init() { }
}
// 3
let singletonPlus = MySingletonPlus.shared

// 4
let singletonPlus2 = MySingletonPlus()

What should you be careful about?

The singleton pattern is very easy to overuse.

Tutorial project

You’ll continue building Rabble Wabble from the previous chapter.

Creating the AppSettings singleton

The first thing you need to do is to have somewhere to store app settings. You’re going to create a singleton for this!

import Foundation

public class AppSettings {
  // MARK: - Static Properties
  public static let shared = AppSettings()
  
  // MARK: - Object Lifecycle
  private init() { }
}
// MARK: - QuestionStrategyType
public enum QuestionStrategyType: Int, CaseIterable {    
  
  case random
  case sequential
  
  // MARK: - Instance Methods    
  public func title() -> String {
    switch self {
    case .random:
      return "Random"
    case .sequential:
      return "Sequential"
    }
  }
  
  public func questionStrategy(
    for questionGroup: QuestionGroup) -> QuestionStrategy {
    switch self {
    case .random:
      return RandomQuestionStrategy(
        questionGroup: questionGroup)
    case .sequential:
      return SequentialQuestionStrategy(
        questionGroup: questionGroup)
    }
  }
}
// MARK: - Keys
private struct Keys {
  static let questionStrategy = "questionStrategy"
}
// MARK: - Instance Properties
public var questionStrategyType: QuestionStrategyType {
  get {
    let rawValue = userDefaults.integer(
      forKey: Keys.questionStrategy)
    return QuestionStrategyType(rawValue: rawValue)!
  } set {
    userDefaults.set(newValue.rawValue,
                     forKey: Keys.questionStrategy)
  }
}
private let userDefaults = UserDefaults.standard
// MARK: - Instance Methods
public func questionStrategy(
  for questionGroup: QuestionGroup) -> QuestionStrategy {
  return questionStrategyType.questionStrategy(
    for: questionGroup)
}

Selecting the strategy

You next need to create a new view controller so the user can select their desired question strategy.

import UIKit

// 1
public class AppSettingsViewController: UITableViewController {
  // 2
  // MARK: - Properties
  public let appSettings = AppSettings.shared
  private let cellIdentifier = "basicCell"

  // MARK: - View Life Cycle
  public override func viewDidLoad() {
    super.viewDidLoad()
    
    // 3
    tableView.tableFooterView = UIView()
    
    // 4
    tableView.register(UITableViewCell.self,
                       forCellReuseIdentifier: cellIdentifier)
  }
}
// MARK: - UITableViewDataSource
extension AppSettingsViewController {

  public override func tableView(
    _ tableView: UITableView,
    numberOfRowsInSection section: Int) -> Int {
    
      // 1
      return QuestionStrategyType.allCases.count
  }
  
  public override func tableView(
    _ tableView: UITableView,
    cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(
      withIdentifier: cellIdentifier, for: indexPath)

    // 2
    let questionStrategyType = 
      QuestionStrategyType.allCases[indexPath.row]

    // 3
    cell.textLabel?.text = questionStrategyType.title()

    // 4
    if appSettings.questionStrategyType == 
      questionStrategyType {
      cell.accessoryType = .checkmark
    } else {
      cell.accessoryType = .none
    }
    return cell
  }
}
// MARK: - UITableViewDelegate
extension AppSettingsViewController {
  public override func tableView(
    _ tableView: UITableView,
    didSelectRowAt indexPath: IndexPath) {

    let questionStrategyType = 
      QuestionStrategyType.allCases[indexPath.row]
    appSettings.questionStrategyType = questionStrategyType
    tableView.reloadData()
  }
}

private let appSettings = AppSettings.shared
viewController.questionStrategy =
  SequentialQuestionStrategy(
    questionGroup: selectedQuestionGroup)
viewController.questionStrategy =
      appSettings.questionStrategy(for: selectedQuestionGroup)

Key points

You learned about the singleton pattern in this chapter. Here are its key points:

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now