I have a Settings struct that stores hundreds of settings in UserDefaults
.
Code below:
struct Settings {
public enum key: String
{
case mySetting = "mySetting"
...
...
}
public var mySetting: Bool {
get {
return UserDefaults.standard.bool(forKey: key.mySetting.rawValue)
}
set (newValue) {
return UserDefaults.standard.set(newValue, forKey: key.mySetting.rawValue)
}
}
....
....
}
I want to extend it. I want to fetch settings of another instance of the app running on a peer device and store it as a Dictionary
. mySetting
must them return the value of a key.mySetting key
from that dictionary
rather than UserDefaults
. But if there is no peer device connected, then it should return the value in UserDefaults
.
I am wondering how I can extend the Settings struct (or perhaps change it to class if needed) to achieve this functionality without putting 'if then else' in hundreds of places in the code.
I do this in hundreds of place:
var settings = Settings()
Or
let settings = Settings()
Some construct that automatically pulls the right settings dictionary to look for would be best.
Edit: As I said not all return types can be simple which makes the job harder. Some return types are enum, some dictionary, some Array. As some suggest to have one unified method that returns Any?, the job would be harder at places where I use the return type, for instance.
public var microphone: Microphone {
get {
if let mic = Microphone.init(rawValue: UserDefaults.standard.integer(forKey: key.microphone.rawValue)) {
return mic
} else {
return .auto
}
}
set (newValue) {
UserDefaults.standard.set(newValue.rawValue, forKey: key.microphone.rawValue)
}
}
I would add an optinal property for the dictionary and then using a pair of get and set functions either use the dictionary or UserDefaults
var peerDevice: [String: Any]?
func setting(forKey key: Key) -> Any? {
if let dictionary = peerDevice {
return dictionary[key.rawValue]
}
return UserDefaults.standard.value(forKey: key.rawValue)
}
mutating func set(_ value: Any, forKey key: Key) {
if peerDevice != nil {
peerDevice?[key.rawValue] = value
} else {
UserDefaults.standard.set(value, forKey: key.rawValue)
}
}
Example of wrapper method for enums using raw values
mutating func setEnum<T>(_ value: T, forKey key: Key) where T: RawRepresentable {
set(value.rawValue, forKey: key)
}
func setting<T>(forKey key: Key, andEnumType: T.Type) -> T? where T: RawRepresentable {
if let value = setting(forKey: key) as? T.RawValue {
return T.init(rawValue: value)
}
return nil
}
I took the liberty to rename the enum from key
to Key
Then use it like this
var settings = Settings()
settings.setSetting(true, forKey: .mySetting)
if let flag = settings.setting(forKey: .mySetting) as? Bool {
print(flag)
}
settings.setEnum(Microphone.mic2, forKey: .mic)
if let mic = settings.setting(forKey: .mic, andEnumType: Microphone.self) {
print(mic)
}