You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中借助结构体与DynamicProperty属性包装器管理UserDefaults

在SwiftUI中集中管理UserDefaults并使用简洁语法的实现方案

可以实现!通过自定义遵循DynamicProperty的属性包装器,结合单例的UserSettings结构体,就能在SwiftUI View中直接访问集中管理的UserDefaults属性,无需重复配置默认值或额外的视图绑定声明。

1. 自定义遵循DynamicProperty的UserDefault属性包装器

这个包装器封装了UserDefaults的读写逻辑,同时通过DynamicProperty参与SwiftUI的视图更新周期,自动响应UserDefaults的变化:

import SwiftUI
import Combine

@propertyWrapper
struct UserDefault<T>: DynamicProperty {
    let key: String
    let defaultValue: T
    private let defaults: UserDefaults
    private var cancellables = Set<AnyCancellable>()
    
    init(key: String, defaultValue: T, defaults: UserDefaults = .standard) {
        self.key = key
        self.defaultValue = defaultValue
        self.defaults = defaults
    }
    
    var wrappedValue: T {
        get { defaults.object(forKey: key) as? T ?? defaultValue }
        nonmutating set {
            // 可在这里添加自定义setter逻辑,比如数据校验
            defaults.set(newValue, forKey: key)
        }
    }
    
    // 监听UserDefaults变化,触发视图更新
    func update() {
        NotificationCenter.default.publisher(for: UserDefaults.didChangeNotification)
            .sink(receiveValue: { _ in })
            .store(in: &cancellables)
    }
}

2. 集中管理的UserSettings结构体

在这个结构体里统一定义所有UserDefaults属性,默认值和自定义逻辑都在这里维护:

struct UserSettings {
    // 单例实例,全局访问
    static let shared = UserSettings()
    
    // 示例属性:用户名
    @UserDefault(key: "app_username", defaultValue: "匿名用户")
    var username: String
    
    // 示例属性:深色模式开关
    @UserDefault(key: "app_dark_mode", defaultValue: false)
    var isDarkMode: Bool
    
    // 示例属性:字体大小
    @UserDefault(key: "app_font_size", defaultValue: 16.0)
    var fontSize: CGFloat {
        didSet {
            // 自定义逻辑:字体变化时发送通知
            NotificationCenter.default.post(name: NSNotification.Name("FontSizeUpdated"), object: fontSize)
        }
    }
    
    // 可添加更多属性...
}

3. 在SwiftUI View中直接使用

无需额外声明@ObservedObject或@StateObject,直接通过UserSettings.shared访问属性,属性变化时视图会自动刷新:

struct DemoView: View {
    var body: some View {
        VStack(spacing: 20) {
            Text("当前用户:\(UserSettings.shared.username)")
                .font(.system(size: UserSettings.shared.fontSize))
            
            Toggle("深色模式", isOn: $UserSettings.shared.isDarkMode)
            
            Button("随机修改用户名") {
                UserSettings.shared.username = "用户_\(Int.random(in: 100...999))"
            }
            
            Slider(value: $UserSettings.shared.fontSize, in: 12...24) {
                Text("字体大小")
            }
        }
        .padding()
        .preferredColorScheme(UserSettings.shared.isDarkMode ? .dark : .light)
    }
}

为什么这个方案可行?

  • DynamicProperty协议让属性包装器能够接入SwiftUI的视图更新机制,当UserDefaults发生变化时,SwiftUI会自动标记视图需要重新渲染。
  • 所有UserDefaults的配置(默认值、key、自定义逻辑)都集中在UserSettings里,避免了AppStorage需要在每个视图重复设置默认值的问题,也方便统一维护。

扩展说明

如果需要更复杂的状态联动(比如多个属性变化触发统一逻辑),可以让UserSettings同时遵循ObservableObject,结合@Published使用,但上述方案已经满足简洁语法的核心需求。

内容的提问来源于stack exchange,提问作者Amirca

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 00:50:21