I'm trying to apply the dependency inversion principle to the UserDefaults and also make it as strong-typed as possible.
Suppose that I have the following enum:
enum LocalStorageKeys: String {
case user
case account
}
Then, I need a LocalStorage protocol:
protocol LocalStorage {
func save(key: LocalStorageKeys, value: Any) throws
func get(key: LocalStorageKeys) -> Any?
}
I know that we could use a generic both in save and get methods, but how could I implement it in a way that when using localStorage.save(key: .user, value: user), it must be provided with the User, and only with the User type? Also, how could I do so the compiler can infer that localStorage.get(.user) is of type User?
In the end, I would like to protect myself from doing things like localStorage.save(key: .user, value: someOtherThing)
Thanks!
My solution based on how Apple does its serialisation is to have different key enumerations per type you want to be able to save.
First create a protocol that anything that can be saved (or got back) must adopt
Any type that you want to be able to save must have a
Keynested type that conforms toCodingKey. The latter is only so that I have a consistent way to extract a key as a string from an instance ofKey.Change your protocol as follows
If
T.Keyis an enum, whenever you save a value of typeTyou are automatically restricted to using the enum cases fromT.Key. The reason I putvaluefirst is because it means that when you are typing a call to the function, by the time you get tofor:the Xcode editor has already figured out what the allowable keys are and will give you a completion list to choose from.Here is how you might implement
UserIn the above case, there are two allowed keys for storing a
User.And here is a stub implementation of a
LocalStorageAnd this is what calling it looks like