Hoe kan ik een Swift-enumeratiewaarde opslaan in NSUserDefaults

Ik heb een opsomming als deze:

enum Environment {
    case Production
    case Staging
    case Dev
}

En ik wil een instantie als volgt opslaan in NSUserDefaults:

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

Ik begrijp dat een Swift-enum geen NSObject is, en dat maakt het moeilijk om op te slaan, maar ik weet niet wat de beste manier is om het om te zetten in iets dat kan worden opgeslagen.


Antwoord 1, autoriteit 100%

Het gebruik van rawValue voor de enum is een manier om typen te gebruiken die kunnen worden opgeslagen in NSUserDefaults. Definieer uw enum om een ​​rawValue te gebruiken. Ruwe waarden kunnen tekenreeksen, tekens of een van de typen integers of drijvende-komma’s zijn:

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

U kunt ook rechtstreeks een enum-instantie maken met behulp van de rawValue (die van NSUserDefaults zou kunnen komen), zoals:

let env = Environment(rawValue: "Dev")

U kunt de rawValue (String) uit het enum-object op deze manier extraheren en indien nodig opslaan in NSUserDefaults:

if let myEnv = env {
    println(myEnv.rawValue)
}
func saveEnvironment(environment : Environment){
    NSUserDefaults.standardUserDefaults().setObject(environment.rawValue, forKey: kSavedEnvironmentDefaultsKey)
}

Antwoord 2, autoriteit 29%

Als u gegevens van UserDefaults wilt opslaan/lezen en wat logica wilt scheiden, kunt u dit op de volgende manier doen (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)
       }
    }
}

Dus het opslaan van gegevens in UserDefaults ziet er als volgt uit:

UserDefaultsManager.shared.environment = Environment.Production

En het lezen van gegevens, opgeslagen in UserDefaults op deze manier:

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

Antwoord 3, autoriteit 8%

Gebruik Codableprotocol

Omvang Environmentenum dat voldoet aan het Codable-protocol om waarden te coderen en decoderen als Data.

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

Een wrapper voor 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
    }
}

Gebruik

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

Antwoord 4, autoriteit 2%

Swift 5.1 U kunt een generieke eigenschapwrapper maken, waarbij u Codable gebruikt om waarden in en uit de UserDefaults te transformeren

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
}

Antwoord 5

Hier is nog een alternatief dat gemakkelijk kan worden gebruikt met opsommingen op basis van typen (zoals String, Int enz.) die kunnen worden opgeslagen door 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)
        }
    }
}

Voorbeeld van gebruik:

enum Environment: String {
    case Production
    case Staging
    case Dev
}
@StoredProperty("Environment", defaultValue: .Dev)
var storedProperty: Environment

Antwoord 6

Ik gebruik zoals dit type staging. Kun je dit alsjeblieft proberen, het zal je helpen.

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") ?? "")")

Antwoord 7

Snelle 5.1
U kunt hiervoor een eigenschapwrapper maken

@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
}

En gebruik het gewoon zo

@UserDefaultsLanguageValue(key: .language, defaultValue: LanguageType.en) var language

Other episodes