Hide chapters

Design Patterns by Tutorials

1 min

8. Observer 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 observer pattern lets one object observe changes on another object. Apple added language-level support for this pattern in Swift 5.1 with the addition of Publisher in the Combine framework.

This pattern involves three types:

  1. The subscriber is the “observer” object and receives updates.
  2. The publisher is the “observable” object and sends updates.
  3. The value is the underlying object that’s changed.

Note: this chapter provides a high-level introduction to @Published properties, but it doesn’t get into all of the details or powerful features offered in the Combine framework. If you’d like to learn more Combine, see our book Combine: Asynchronous Programming with Swift (

When should you use it?

Use the observer pattern whenever you want to receive changes made on another object.

This pattern is often used with MVC, where the view controller has subscriber(s) and the model has publisher(s). This allows the model to communicate changes back to the view controller without needing to know anything about the view controller’s type. Thereby, different view controllers can use and observe changes on the same model type.

Playground example

Open FundamentalDesignPattern.xcworkspace in the Starter directory, or continue from your own playground workspace from the last chapter, and then open the Overview page.

// 1
import Combine

// 2
public class User {
    // 3
    @Published var name: String
    // 4
    public init(name: String) { = name
// 1
let user = User(name: "Ray")

// 2
let publisher = user.$name

// 3
var subscriber: AnyCancellable? = publisher.sink() {
    print("User's name is \($0)")

// 4 = "Vicki"
User's name is Ray
User's name is Vicki
subscriber = nil = "Ray has left the building"

What should you be careful about?

Before you implement the observer pattern, define what you expect to change and under which conditions. If you can’t identify a reason for an object or property to change, you’re likely better off not declaring it as var or @Published, and instead, making it a let property.

Tutorial project

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

import Combine
@Published public var runningPercentage: Double = 0
// 1
private enum CodingKeys: String, CodingKey {
  case correctCount
  case incorrectCount

// 2
public required init(from decoder: Decoder) throws {
  let container = try decoder.container(keyedBy: CodingKeys.self)
  self.correctCount = try container.decode(Int.self, forKey: .correctCount)
  self.incorrectCount = try container.decode(Int.self, forKey: .incorrectCount)

// 3
private func updateRunningPercentage() {
  let totalCount = correctCount + incorrectCount
  guard totalCount > 0 else {
    runningPercentage = 0
  runningPercentage = Double(correctCount) / Double(totalCount)
public var correctCount: Int = 0 {
  didSet { updateRunningPercentage() }
public var incorrectCount: Int = 0 {
  didSet { updateRunningPercentage() }
public func reset() {
  correctCount = 0
  incorrectCount = 0
public private(set) var score: Score
self.questionGroupCaretaker.selectedQuestionGroup.score =
import Combine
public var percentageSubscriber: AnyCancellable?
cell.percentageSubscriber =
  questionGroup.score.$runningPercentage // 1
    .receive(on: DispatchQueue.main) // 2
    .map() { // 3
      return String(format: "%.0f %%", round(100 * $0))
  }.assign(to: \.text, on: cell.percentageLabel) // 4

Key points

You learned about the observer 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.
© 2023 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