Singletons in Swift

Singletons sind ein spannendes Pattern; und das gilt für die Programmierung im Allgemeinen. Einfach ausgedrückt bezeichnet ein Singleton einen Typ, von dem es innerhalb eines Projekts nur eine einzige Instanz gibt. Wann immer man also mit diesem Typ arbeiten möchte, greift man auf jenes Singleton zurück.

Das eröffnet einem diverse Vorteile. So kann man sichergehen, dass die Daten, die über das Singleton verwaltet werden, bei jedem Aufruf – egal wann und wo – up-to-date sind. In solch einem Szenario gibt es nicht mehrere Instanzen jenes Typs, die alle ihre eigenen Informationen enthalten die man schlimmstenfalls selbst noch mühsam abgleichen muss.

Zur Erläuterung möchte ich ein kleines Beispiel nennen, das aus dem Foundation-Framework von Apple selbst stammt: Mithilfe der Klasse UserDefaults bildet man typischerweise plattformübergreifend Nutzereinstellungen ab, die in einer Art Key-Value-Store gespeichert werden. Zu diesem Zweck verfügt die UserDefaults-Klasse über eine Type Property namens standard. Wann immer man die Nutzereinstellungen verändern oder auslesen möchte, greift man typischerweise über jene Property auf die Singleton-Instanz von UserDefaults zurück und stellt damit sicher, immer und überall das aktuelle und korrekte Set an Einstellungen zu erhalten.

Man stelle sich einmal vor, es gäbe mehrere UserDefaults-Instanzen die sich innerhalb einer App verteilen, jede davon mit unterschiedlichen Einstellungen versehen. Das wäre nicht nur extrem schwer zu handhaben (und damit enorm fehleranfällig), es wäre auch unnötig kompliziert. Anders formuliert: Warum sollte man mehrere verschiedene Varianten von Nutzereinstellungen in einer App speichern und verwalten? Ein solches Element macht nur einmalig Sinn, weshalb auch das Singleton-Pattern im genannten Fall mehr als angebracht ist.

Deklaration im Code

So viel zu den Hintergründen. Im Folgenden betrachten wir, wie man selbst einen Typ erstellt, der als Singleton verwendet werden soll. Wer diesbezüglich bereits Erfahrung mit Objective-C gesammelt hat, kann an dieser Stelle aufatmen: Singletons in Swift lassen sich im Vergleich sehr einfach und mit nur einem einzigen Befehl erstellen.

Basis für das Singleton-Pattern sind Type Properties, genau wie beim genannten Beispiel der UserDefaults. Diese Property liefert eine Instanz des zugrundeliegenden Typs zurück, bei der es sich um das eigentliche Singleton handelt; eine Instanz, die einmalig innerhalb eines gesamten Projekts existiert. Alles, was man in Swift tun muss, um solch eine Type Property korrekt zu definieren, ist, sie mit dem static-Schlüsselwort zu versehen und ihr eine Instanz des zugrundeliegenden Typs zuzuweisen.

Ein Beispiel hierfür zeigt das folgende Listing, in dem eine Klasse namens Settings zur Abbildung von Einstellungen deklariert wird. Der Einfachheit halber enthält sie nur eine Eigenschaft namens useDarkMode und eine Methode namens changePresentationMode().

class Settings {
    
    // Properties
    static let shared = Settings()
    var useDarkMode = true
    
    // Methods
    func changePresentationMode() {
        useDarkMode = !useDarkMode
        print("Presentation mode changed.")
    }
}

Möchte man nun innerhalb eines zugehörigen Projekts die Klasse Settings nutzen, greift man auf sie über die shared-Property zu:

Settings.shared.useDarkMode = false

Änderungen, die man so am Singleton durchführt, gelten umgehend für alle anderen Stellen im Projekt, in denen man auf die entsprechenden Eigenschaften und Funktionen zugreift.

Erstellen weiterer Instanzen verhindern

Prinzipiell wurde in dem genannten Beispiel das Singleton-Pattern umgesetzt und hat zur Folge, dass die Klasse Settings über die mittels shared zurückgelieferte Instanz eingesetzt werden kann. Allerdings ist es noch immer prinzipiell möglich, weitere Instanzen von Settings zu erstellen und anderweitig einzusetzen, was mit Singletons nicht mehr viel zu tun hat.

Möchte man das Singleton-Pattern auf die Spitze treiben, muss man jene Möglichkeit unterbinden. Das lässt sich lösen, indem man den Initializer der Settings-Klasse mittels Access Control als private deklariert. Außerhalb der Klasse selbst können dann keinerlei Settings-Instanzen mehr innerhalb eines Projekts erzeugt werden.

class Settings {
    
    // Properties
    static let shared = Settings()
    var useDarkMode = true
    
    // Initializer
    private init() {}
    
    // Methods
    func changePresentationMode() {
        useDarkMode = !useDarkMode
        print("Presentation mode changed.")
    }
}

Fazit

Ob Singletons für einen Typ ein angebrachtes Pattern sind, muss von Fall zu Fall entschieden werden. Falls man sich aber für den Einsatz entscheidet, macht es Swift einem so leicht wie kaum eine andere Programmiersprache, Singletons umzusetzen. Gerade im Vergleich zu Objective-C ist das Vorgehen zur Deklaration von Singletons nicht nur deutlich einfacher, sondern auch wesentlich besser lesbar.

Euer Thomas


Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert