我正在try 将依赖关系倒置原则应用于UserDefaults,并使其尽可能地具有强类型.

假设我有以下枚举:

enum LocalStorageKeys: String {
    case user
    case account
}

然后,我需要一个LocalStorage%的方案:

protocol LocalStorage {
    func save(key: LocalStorageKeys, value: Any) throws
    func get(key: LocalStorageKeys) -> Any?
}

我知道我们可以在SAVE和GET方法中使用泛型,但当使用localStorage.save(key: .user, value: user)时,它必须与User一起提供,并且只能与User类型一起提供,我该如何实现它呢?另外,我怎么做才能让编译器推断出localStorage.get(.user)User类型呢?

最后,我想保护自己不做像localStorage.save(key: .user, value: someOtherThing)这样的事情

谢谢!

推荐答案

我的解决方案基于苹果如何进行序列化,我的解决方案是让你想要保存的每种类型都有不同的键枚举.

首先创建一个协议,任何可以保存(或恢复)的东西都必须采用该协议

protocol Saveable
{
    associatedtype Key: CodingKey
}

您希望能够保存的任何类型都必须具有符合CodingKeyKey嵌套类型.后者只是为了让我有一种一致的方法从Key个实例中提取一个字符串形式的密钥.

按如下方式更改您的协议

protocol LocalStorage
{
    func save<T: Saveable>(value: T, for key: T.Key)
    func get<T: Saveable>(type: T.Type, key: T.Key) -> T?
}

如果T.Key是枚举,则无论何时保存T类型的值,都会自动限制使用T.Key中的枚举大小写.我把value放在第一位的原因是,这意味着当您输入对函数的调用时,当您到达for:时,Xcode编辑器已经计算出允许的键是什么,并将给您一个可供 Select 的完成列表.

下面是您可以如何实现User


struct User
{
    var name: String
}

extension User: Saveable
{
    enum Key: String, CodingKey
    {
        case user
        case otherUser
    }
}

在上面的情况下,有两个允许的密钥用于存储User.

下面是LocalStorage的存根实现


struct StorageImplementation: LocalStorage
{
    func save<T: Saveable>(value: T, for key: T.Key)
    {
        print(key.stringValue)
    }

    func get<T: Saveable>(type: T.Type, key: T.Key) -> T?
    {
        print(key.stringValue)
        return nil
    }
}

这就是打电话给它的样子

let user = User(name: "jeremy")
StorageImplementation().save(value: user, for: .otherUser) // prints otherUser
StorageImplementation().get(type: User.self, key: .user) // prints user

Swift相关问答推荐

为什么Swift在某些链调用中不能对不可变值使用变异成员,而在其他链调用中则不能使用变异成员?

通常从数组调用的SWIFT静态协议函数

从任务中打印

除法中如果除以完全因数需要更长时间吗?

Xcode 15 Beta中如何使用@Observable?

解码 JSON 时 Swift Struct Decoder 初始化程序错误

如何从我的 macOS 应用程序打开 (.log) 文件?

swift 是否遇到 Java 的 2gb 最大序列化大小问题?

在 macOS (Swift) 上获取 BSD 驱动器名称的最佳方法是什么?

按钮图像不会立即刷新

SwiftUI:异步网络请求后@State 值不更新

如何使 SwiftUI Picker 换行文本

macOS 守护进程应该由Command Line ToolXcode 模板制作吗?

在 Swift 4 中实现自定义解码器

AES 加密和解密

Xcode 6 中的嵌入式内容包含 Swift 代码构建设置有什么作用?

为什么 swift 中的类没有存储类型属性?

自动布局:获取 UIImageView 高度以正确计算单元格高度

如何从 UITableViewCell 类中引用 UITableViewController?

TabView 在切换选项卡时重置导航堆栈