Press ESC to close

Swift Observer

Hello friends, in this article we will talk about how to set up an Observer structure with Swift. First of all, we can start with why we need a structure. Couldn’t we do it with NotificationCenter?

When a place or an object changes in your project, the classes that are directly or indirectly connected to it need to be informed. We used to manage this situation with Notification Center. Notification Center is difficult to manage and can be triggered or listened to from anywhere without any restrictions. This can cause big problems in big projects. Because you don’t have any restrictions. At the same time, it is an old structure in terms of usage. Therefore, it makes much more sense to set up an Observer structure.

In the example I will give in this article, I will actually give an example from an application that is live. In my application called Fintracker – Budget Saver, when the exchange rate changes, the other pages need to be informed about it and take action accordingly. Because we don’t want the exchange rate to remain with the old exchange rate despite the exchange rate changing. For this reason, if the exchange rate changes, everything related to it needs to change.

First of all, we create a protocol for this process. I created a protocol called LocallyCurrencyUpdaterObserver. This protocol is actually for objects that listen for currency changes. Classes that want to listen for currency changes have to implement this protocol.

As a second step, we create a protocol called ObservableLocallyCurrencyUpdater. With this protocol, there are functions that actually hold observers. They add themselves as observers and delete them from observers, and also notify observers. We also need to write an extension for this protocol. We implement it by default with this extension.

protocol LocallyCurrencyUpdaterObserver: AnyObject {
    func onCurrencyUpdated()
}

protocol ObservableLocallyCurrencyUpdater: AnyObject {
    var observers: [LocallyCurrencyUpdaterObserver] { get set }

    func addObserver(_ observer: LocallyCurrencyUpdaterObserver)
    func removeObserver(_ observer: LocallyCurrencyUpdaterObserver)
    func notifyObservers()
}

extension ObservableLocallyCurrencyUpdater {
    func addObserver(_ observer: LocallyCurrencyUpdaterObserver) {
        observers.append(observer)
    }

    func removeObserver(_ observer: LocallyCurrencyUpdaterObserver) {
        if let index = observers.firstIndex(where: { $0 === observer }) {
            observers.remove(at: index)
        }
    }

    func notifyObservers() {
        observers.forEach { $0.onCurrencyUpdated() }
    }
}

After creating the protocols, we can start the implementation. I have a class that I use to update currency-related transactions. Although its naming is not nice, it does the job. This class actually manages all currency transactions. This class should implement the protocol we created above. In this way, it will be able to manage the observers. If you noticed, we did not implement any function of this protocol. Because we already did this with the extension. If the user changes their currency preference, it calls the function in this class and the change is made via the local database. After it is provided, it notifies the observers.

final class CurrencyHelper: ObservableLocallyCurrencyUpdater {
    var observers: [LocallyCurrencyUpdaterObserver] = []

    let currencies: [Currency] = [.dollar, .euro, .turkishLira, .gramGold]

    func changeDefaultCurrency(currency: Currency) {
        SettingsService.updateDefaultCurrency(currency: currency)
        notifyObservers()
    }
}

In order for a class or ViewController to add itself as an observer here, it needs to implement the LocallyCurrencyUpdaterObserver protocol. It should also add itself as an observer. After an implementation like the one below, the result is great.

import UIKit

final class StatsViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        CurrencyHelper.shared.addObserver(self)
        configure()
    }

    deinit {
        CurrencyHelper.shared.removeObserver(self)
    }
}

// MARK: - LocallyCurrencyUpdaterObserver
extension StatsViewController: LocallyCurrencyUpdaterObserver {
    func onCurrencyUpdated() {
        tableView.reloadData()
    }
}

If you have questions or different thoughts, you can reach me by sending an e-mail or commenting. Good works.

Leave a Reply

Your email address will not be published. Required fields are marked *