How can I store a Swift enum value in NSUserDefaults

Using Codable protocol

Extent Environment enum that conforms to Codable protocol to encode and decode values as Data.

enum Environment: String, Codable {
    case Production
    case Staging
    case Dev
}

A wrapper for UserDefaults:

struct UserDefaultsManager {
    static var userDefaults: UserDefaults = .standard
    
    static func set<T>(_ value: T, forKey: String) where T: Encodable {
        if let encoded = try? JSONEncoder().encode(value) {
            userDefaults.set(encoded, forKey: forKey)
        }
    }
    
    static func get<T>(forKey: String) -> T? where T: Decodable {
        guard let data = userDefaults.value(forKey: forKey) as? Data,
            let decodedData = try? JSONDecoder().decode(T.self, from: data)
            else { return nil }
        return decodedData
    }
}

Usage

// Set
let environment: Environment = .Production
UserDefaultsManager.set(environment, forKey: "environment")

// Get
let environment: Environment? = UserDefaultsManager.get(forKey: "environment")


Here is another alternative that can be be easily used with enums based on types (like String, Int etc) that can be stored by NSUserDefaults.

@propertyWrapper
struct StoredProperty<T: RawRepresentable> {
    let key: String
    let defaultValue: T

    init(_ key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    var wrappedValue: T {
        get {
            guard let rawValue = UserDefaults.standard.object(forKey: key) as? T.RawValue, let value = T(rawValue: rawValue) else {
                 return defaultValue
            }
            return value
        }
        set {
            UserDefaults.standard.set(newValue.rawValue, forKey: key)
        }
    }
}

Example usage:

enum Environment: String {
    case Production
    case Staging
    case Dev
}

@StoredProperty("Environment", defaultValue: .Dev)
var storedProperty: Environment

If you would like to save/read data from UserDefaults and separate some logic, you can do it in following way (Swift 3):

enum Environment: String {
    case Production
    case Staging
    case Dev
}

class UserDefaultsManager {

    static let shared = UserDefaultsManager()

    var environment: Environment? {
       get {
           guard let environment = UserDefaults.standard.value(forKey: kSavedEnvironmentDefaultsKey) as? String else {
               return nil
           }
           return Environment(rawValue: environment)
       }
       set(environment) {
           UserDefaults.standard.set(environment?.rawValue, forKey: kSavedEnvironmentDefaultsKey)
       }
    }
}

So saving data in UserDefaults will look this way:

UserDefaultsManager.shared.environment = Environment.Production

And reading data, saved in UserDefaults in this way:

if let environment = UserDefaultsManager.shared.environment {
    //you can do some magic with this variable
} else {
    print("environment data not saved in UserDefaults")
}

Using rawValue for the enum is one way of using types that can be stored in NSUserDefaults, define your enum to use a rawValue. Raw values can be strings, characters, or any of the integer or floating-point number types :

enum Environment: String {
    case Production = "Prod"
    case Staging    = "Stg"
    case Dev        = "Dev"
}

You can also create an enum instance directly using the rawValue (which could come from NSUserDefaults) like:

let env = Environment(rawValue: "Dev")

You can extract the rawValue (String) from the enum object like this and then store it in NSUserDefaults if needed:

if let myEnv = env {
    println(myEnv.rawValue)
}


func saveEnvironment(environment : Environment){
    NSUserDefaults.standardUserDefaults().setObject(environment.rawValue, forKey: kSavedEnvironmentDefaultsKey)
}

Tags:

Swift