How to create a Settings manager in Swift 5.1

Think that you need to work with UserDefaults to save if user is logged in, or if onboarding is already showed.

Your code looks like it:

let userDefaults = UserDefaults.standard
userDefaults.set(logged, forKey: "isLoggedIn")

if UserDefaults.standard.bool(forKey: "isLoggedIn") {
	print("User Logged")
}

My propose is replace for it:

AppSettings[.isLoggedIn] = true
if AppSettings.boolValue(.isLoggedIn) {
    print("User Logged")
}

Pros:

  • Pattern with property names;
  • If you need change UserDefaults to a Database (or something else) you will need change 2 lines;
  • It’s clear to Understand AppSettings — SOLID
  • Setting value it’s easy
  • Getting value it’s type safety

Cons:

  • Need Swift 5.1.

Let’s code:

Create a Playground:

Import Foundation:

import Foundation

Now we will create an Enum! Yes an Enum!

public enum AppSettings {

}

The next step is create a subscript method:

public enum AppSettings {
	public static subscript(_ key: String) -> Any? {
        get {
            return UserDefaults.standard.value(forKey: key)
        }
        set {
            UserDefaults.standard.setValue(newValue, forKey: key)
        }
    }
}

Running this code you can use:

AppSettings["isLogged"] = true
if let logged = AppSettings["isLogged"] as? Bool, logged {
	print("User Logged")
}

Ok… but is not beautiful 🙁 But wait!

Now we will create a New Enum inside AppSettings AND make some changes:

public enum AppSettings {

	// New enum with the Keys, add all settings key here
	public enum key: String {
            case isLoggedIn = "isLoggedIn"
            case userName = "userName"
	}

	public static subscript(_ key: key) -> Any? { // the parameter key have a enum type `key`
        get { // need use `rawValue` to acess the string
            return UserDefaults.standard.value(forKey: key.rawValue) 
        }
        set { // need use `rawValue` to acess the string
            UserDefaults.standard.setValue(newValue, forKey: key.rawValue)
        }
    }
}

Running this code you can use:

AppSettings2[.isLoggedIn] = true
if let logged = AppSettings2[.isLoggedIn] as? Bool, logged {
    print("User Logged")
}

Ok, more useful, with patterns but I still unwrap and verify the type. (as? Bool)

The solution… It’s here:

extension AppSettings {
    public static func boolValue(_ key: key) -> Bool {
        if let value = AppSettings[key] as? Bool {
            return value
        }
        return false
    }
    
    public static func stringValue(_ key: key) -> String? {
        if let value = AppSettings[key] as? String {
            return value
        }
        return nil
    }
    
    public static func intValue(_ key: key) -> Int? {
        if let value = AppSettings[key] as? Int {
            return value
        }
        return nil
    }
}

An extension that convert to types, like UserDefaults do.

Now our code looks like:

AppSettings[.isLoggedIn] = true
if AppSettings.boolValue(.isLoggedIn) {
    print("User Logged")
}
// Print: User Logged

You can use and separate enum with key, to have a file with settings keys.

I hope this helps you, if you have questions of suggestions, comment here or call me in my social networks.