Event-driven architecture in Swift: turning code into an orchestra of events

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why does your Swift app need event-driven architecture?



Imagine you are the conductor of an orchestra. Each musician waits for their part to join in at the right moment. If the violinists played all at once — it would be chaos. This is roughly how event-driven architecture in Swift works: each module only reacts to specific events, rather than pulling other modules' strings directly.



When I first encountered event-driven programming in Swift in a commercial project, I realized how powerful a tool it is. A food delivery app: the user pressed the "Order" button — that's an event. The cart updated — an event. A push notification about the courier arrived — an event. Each module (UI, network, local DB) simply subscribes to the needed events and works independently. That is event-driven architecture.



In this article, we will break down three approaches to implementing Swift event architecture: classic delegates, NotificationCenter, and a custom event bus. You will see real code, understand when to use what, and be able to apply it immediately in your project. Let's go!



Fundamentals of the event-driven approach in Swift



What is an event in the context of Swift?



An event is a signal that something has happened. It can be:

  • User input (button pressed, text changed)
  • Data changes (data arrived from the server, database updated)
  • System notifications (app went to background, keyboard appeared)


The main principle of event-driven architecture in Swift is loose coupling. The event sender does not know who will handle it. The receiver does not know who sent it. They communicate through an event bus — this makes the code flexible and testable.



The Observer pattern — the foundation of events



The Observer pattern in Swift is the basis of any event-driven system. We have one Publisher object and many Subscribers. When the publisher changes state, it notifies all subscribers.



In Swift, there are several built-in mechanisms for this:

  • NotificationCenter — a global event bus in the iOS SDK
  • Delegates — one-to-one, but with control
  • Combine — a reactive framework from Apple (iOS 13+)
  • Custom implementations — full control over behavior


Let's break down each approach with examples.



Method 1. Delegates: the classic approach



Delegates in Swift are the simplest way to implement an event. It is suitable for one-to-one relationships, where one object notifies another about its state.



Example: imagine we have an image loader that needs to notify a controller upon completion.



// 1. Define the delegate protocolprotocol ImageLoaderDelegate: AnyObject {    func imageLoader(_ loader: ImageLoader, didLoad image: UIImage)    func imageLoader(_ loader: ImageLoader, didFailWith error: Error)}

// 2. Publisher classclass ImageLoader { weak var delegate: ImageLoaderDelegate?

func loadImage(from url: URL) { URLSession.shared.dataTask(with: url) { [weak self] data, _, error in DispatchQueue.main.async { if let error = error { self?.delegate?.imageLoader(self!, didFailWith: error) } else if let data = data, let image = UIImage(data: data) { self?.delegate?.imageLoader(self!, didLoad: image) } } }.resume() }}

// 3. Subscriber controllerclass ViewController: UIViewController, ImageLoaderDelegate { let loader = ImageLoader() @IBOutlet weak var imageView: UIImageView!

override func viewDidLoad() { super.viewDidLoad() lo

Blogs

Book Recommendations