'Best practice for storing generic preferences in Realm / Swift

I'm wondering what the best practice is for building a general user preferences mechanism that is backed by RealmSwift.

Ideally the solution will be key/value based and easily extended. My initial thinking on a basic implementation:

protocol DefaultsProvider {
   func get(_ key: AvailableDefaults) -> Any?
   func get(_ key: String) -> Any?
}

enum AvailableDefaults: String {
   case userColorPreference
   case otherDefaultThing
}

struct DefaultsManager: DefaultsProvider {
   private var defaults: Dictionary<AvailableDefaults, Any> = [:]
   
   func get(_ key: AvailableDefaults) -> Any? { defaults[key] }
   func get(_ key: String) -> Any? {
      guard let key = AvailableDefaults(rawValue: key) else { return nil }
      return defaults[key]
   }
}

As an API this is workable and ultimately extensible. For instance, I can easily store, say, an array of enumerated values ([.blue, .green]) as a preference. But it has a couple shortcomings:

  1. It requires casting results to/from Any, e.g., to get back that array of enumerations I'd do: defaults.get(.userColorPreference) as! [Colors]). I'm not loving that.
  2. It gets nastier when we look at how to persist this in Realm, since Any is not a persistable type. I could wrap default in something that is persistable. For instance:
import RealmSwift
class DefaultValue: Object {
   let value: CustomStringConvertible
   
   // Convenience   
   var asColors: [Colors]? { get { ... }}
}

The above would allow me to persist anything that is CustomStringConvertible (and give me the flexibility to add some convenience functions to the class). Obviously, I could also just put a bunch of convenience functions on the DefaultsProvider, such as getUserColorPrefs.

But the further I go down this path, the more I start to wonder if it just makes more sense to store a struct Defaults or maybe a JSON object in Realm, and be done with it, but that's definitely moving away from a generic, highly extensible defaults provider.



Solution 1:[1]

Looks like you want to try Realm-Swift Type Projections - it allow you to cast Realm-persistable types to whatever you need https://www.mongodb.com/developer/how-to/type-projections/

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pavel Yakimenko