SwiftUI 中的 @AppStorage 如何使用

9,519 阅读3分钟

什么是 @AppStorage?

@AppStorage 是一个属性包装器,用于在 SwiftUI 应用中简化对用户默认设置(UserDefaults)的读写操作。通过使用 @AppStorage,开发者可以直接将值绑定到用户默认设置中,从而轻松地保存和读取数据,而无需手动管理 UserDefaults。

@AppStorage 的基本用法

下面我们通过一个简单的示例,来介绍下基本用法。在这个示例中,我们将实现一个开关按钮,用于切换应用程序的主题,并将其状态持久化存储。

import SwiftUI

struct ContentView: View {
    @AppStorage("isDarkMode") private var isDarkMode: Bool = false

    var body: some View {
        VStack {
            Toggle(isOn: $isDarkMode) {
                Text("Dark Mode")
            }
            .padding()
        }
        .preferredColorScheme(isDarkMode ? .dark : .light)
    }
}

代码解读:

  • 声明 @AppStorage 变量:使用 @AppStorage 属性包装器声明一个 isDarkMode 变量,并指定其键为 "isDarkMode"。如果该键不存在,则默认值为 false。
  • 绑定 Toggle 组件:我们将 Toggle 组件的 isOn 属性绑定到 @AppStorage 变量 $isDarkMode。当用户切换开关时,isDarkMode 的值会自动更新,并保存到用户默认设置中。
  • 设置颜色模式:使用 preferredColorScheme 修饰符,根据 isDarkMode 的值设置应用程序的颜色模式。

如果你需要删除某个 @AppStorage 变量对应的值,可以使用 UserDefaultsremoveObject 方法:

UserDefaults.standard.removeObject(forKey: "isDarkMode")

使用 @AppStorage 的优点

其优点主要体现在以下三点:

  • 简洁性:简化了数据存储代码,使得代码更加简洁明了,开发者无需再去操作 UserDefaults。
  • 自动更新:当存储的数据发生变化时,绑定到这些数据的 UI 组件会自动更新。
  • 类型安全:@AppStorage 确保数据类型的一致性,避免了类型转换错误。

当然,它还是有一些不足之处的,下面来说一下 @AppStorage 的缺点。

@AppStorage 的不足之处

第一点就是其支持的类型很少,目前仅支持:Bool、Int、Double、String、URL、Data。常用的 Array 并不支持。但我们可以通过自己给 UserDefaults 添加扩展来避免这个问题:

extension Array: RawRepresentable where Element: Codable {
    public init?(rawValue: String) {
        guard let data = rawValue.data(using: .utf8),
              let result = try? JSONDecoder().decode([Element].self, from: data)
        else { return nil }
        self = result
    }

    public var rawValue: String {
        guard let data = try? JSONEncoder().encode(self),
              let result = String(data: data, encoding: .utf8)
        else {
            return "[]"
        }
        return result
    }
}

使用方式和基础类型使用方式一致:

@AppStorage("userIds") private var userIds = [3,4,5]

第二点是数据安全的问题。 UserDefaults 并不是为存储敏感数据设计的。尽管可以对数据进行加密后存储,但 @AppStorage 并未内置这些功能,开发者需要额外处理数据的加密和解密。所以尽量不要保存和隐私有关的重要数据。

第三点是数据同步的问题。操作系统为了效率的考量,UserDefaults 中的数据在发生变化时并不会立马去进行持久化,而是系统会在认为合适的时机才将数据保存在硬盘中。因此,可能发生数据不能完全同步的情况,严重时有数据彻底丢失的可能。尽量不要在其中保存会影响 App 执行完整性的关键数据,在出现数据丢失的状况下,App 仍可根据默认值正常运行。

最后一点,因为使用 @AppStorage 时需要将 key 以字符串的形式传进去,这样可能会导致拼写错误而发生问题。这个可以通过以下方式来避免:

enum UserConfiguration {
    static let userName = AppStorage(wrappedValue: "jack", "userName")
    static let userAge = AppStorage(wrappedValue: 20, "userAge")
}

使用方式如下:

struct ContentView: View {
    private let userName = UserConfiguration.userName
    private let userAge = UserConfiguration.userAge
    var body: some View {
        Text(userName.wrappedValue)
        Text("\(userAge.wrappedValue)")
    }
}

总结

@AppStorage 是 SwiftUI 中一个好用的属性包装器,它简化了用户默认设置的读写操作,并提供了类型安全的绑定机制。在实际开发中,@AppStorage 可以用于保存用户偏好设置、应用状态等数据,使得开发更加高效和便捷。