How can I store a Swift enum value in NSUserDefaults

前端 未结 7 738
猫巷女王i
猫巷女王i 2021-01-31 07:47

I have an enum like this:

enum Environment {
    case Production
    case Staging
    case Dev
}

And I\'d like to save an instance in NSUserDef

相关标签:
7条回答
  • 2021-01-31 07:55

    Swift 5.1 You can create a generic property wrapper, using Codable to transform values in and out the UserDefaults

    extension UserDefaults {
        // let value: Value already set somewhere
        // UserDefaults.standard.set(newValue, forKey: "foo")
        //
        func set<T>(_ value: T, forKey: String) where T: Encodable {
            if let encoded = try? JSONEncoder().encode(value) {
                setValue(encoded, forKey: forKey)
            }
        }
    
        // let value: Value? = UserDefaults.standard.get(forKey: "foo")
        //
        func get<T>(forKey: String) -> T? where T: Decodable {
            guard let data = value(forKey: forKey) as? Data,
                let decodedData = try? JSONDecoder().decode(T.self, from: data)
                else { return nil }
            return decodedData
        }
    }
    
    @propertyWrapper
    public struct UserDefaultsBacked<Value>: Equatable where Value: Equatable, Value: Codable {
        let key: String
        let defaultValue: Value
        var storage: UserDefaults = .standard
    
        public init(key: String, defaultValue: Value) {
            self.key = key
            self.defaultValue = defaultValue
        }
    
        // if the value is nil return defaultValue
        // if the value empty return defaultValue
        // otherwise return the value
        //
        public var wrappedValue: Value {
            get {
                let value: Value? = storage.get(forKey: key)
                if let stringValue = value as? String, stringValue.isEmpty {
                    // for string values we want to equate nil with empty string as well
                    return defaultValue
                }
                return value ?? defaultValue
            }
            set {
                storage.set(newValue, forKey: key)
                storage.synchronize()
            }
        }
    }
    
    // use it
    struct AppState: Equatable {
        enum TabItem: String, Codable {
            case home
            case book
            case trips
            case account
        }
        var isAppReady = false
    
        @UserDefaultsBacked(key: "selectedTab", defaultValue: TabItem.home)
        var selectedTab
        // default value will be TabItem.home
    
        @UserDefaultsBacked(key: "selectedIndex", defaultValue: 33)
        var selectedIndex
        // default value will be 33
    
    }
    
    
    0 讨论(0)
  • 2021-01-31 08:00

    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
    
    0 讨论(0)
  • 2021-01-31 08:02

    Swift 5.1 You can create property wrapper for this

    @propertyWrapper final class UserDefaultsLanguageValue {
        var defaultValue: LanguageType
        var key: UserDefaultsKey
    
        init(key: UserDefaultsKey, defaultValue: LanguageType) {
            self.key = key
            self.defaultValue = defaultValue
        }
    
        var wrappedValue: LanguageType {
            get { LanguageType(rawValue: UserDefaults.standard.object(forKey: key.rawValue) as? String ?? defaultValue.rawValue) ?? .en }
            set { UserDefaults.standard.set(newValue.rawValue, forKey: key.rawValue) }
        }
    }
    
    enum UserDefaultsKey: String {
        case language
    }
    
    enum LanguageType: String {
        case en
        case ar
    }
    

    And use it just like that

    @UserDefaultsLanguageValue(key: .language, defaultValue: LanguageType.en) var language
    
    0 讨论(0)
  • 2021-01-31 08:07

    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)
    }
    
    0 讨论(0)
  • 2021-01-31 08:09

    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")
    
    
    0 讨论(0)
  • 2021-01-31 08:12

    I am using like this type staging. Can you please try this it will help you.

    enum Environment: String {
      case Production  = "Production URL"
      case Testing     = "Testing URl"
      case Development = "Development URL"
    }
    //your button actions
     // MARK: set Development api
      @IBAction func didTapDevelopmentAction(_ sender: Any) {
        let env = Environment.Development.rawValue
        print(env)
        UserDefaults.standard.set(env, forKey:Key.UserDefaults.stagingURL)
      }
    // MARK: set Production api
      @IBAction func didTapProductionAction(_ sender: Any) {
        let env = Environment.Production.rawValue
        print(env)
        UserDefaults.standard.set(env, forKey:Key.UserDefaults.stagingURL)
      }
    
      // MARK: set Testing api
      @IBAction func didTapTestingAction(_ sender: Any) {
        let env = Environment.Testing.rawValue
        print(env)
        UserDefaults.standard.set(env, forKey:Key.UserDefaults.stagingURL)
      }
    //Based on selection act
    print("\(UserDefaults.standard.object(forKey: "stagingURL") ?? "")")
    
    0 讨论(0)
提交回复
热议问题