Swift文件间共享UserDefaults的问题排查
问题排查与解决方案
你的标签显示空白,核心问题出在UserDefaults未正确初始化、变量不同步以及作用域隔离这几个方面,下面一步步帮你解决:
1. 先修复拼写错误(避免混淆)
你多处把defaults写成了defualts,虽然这不是语法错误,但遵循标准拼写能减少后续维护的混乱,建议统一改成defaults。
2. 正确初始化UserDefaults默认值
你的setDefualts()方法从来没有被调用过,导致第一次启动时UserDefaults里没有对应的键,double(forKey:)会返回默认的0。更推荐用register(defaults:)方法来设置默认值——它不会覆盖已存储的值,只在键不存在时生效,比手动调用set更安全。
3. 避免全局变量,让数据和UserDefaults实时同步
你在AppUtility外定义的全局变量,只会在类加载时读取一次UserDefaults,修改后也不会写回,而且TodayViewController里的变量是独立的,和全局变量完全隔离,这就导致点击操作后,视图控制器的标签永远显示初始值。
我们可以把这些属性封装到AppUtility里,用计算属性来实时读取和写入UserDefaults,确保所有地方获取的都是最新值。
修改后的完整代码
AppUtility.swift
import Foundation class AppUtility { // 用计算属性同步UserDefaults private static let defaults = UserDefaults.standard // 初始化默认值,建议在App启动时调用一次 static func setupDefaults() { let defaultValues: [String: Any] = [ "bits": 0.0, "bitsPerClick": 1.0, "upgradeCost": 10.0 ] defaults.register(defaults: defaultValues) } static var bits: Double { get { defaults.double(forKey: "bits") } set { defaults.set(newValue, forKey: "bits") } } static var bitsPerClick: Double { get { defaults.double(forKey: "bitsPerClick") } set { defaults.set(newValue, forKey: "bitsPerClick") } } static var upgradePrice: Double { get { defaults.double(forKey: "upgradeCost") } set { defaults.set(newValue, forKey: "upgradeCost") } } static func upgradeClick() { if bits >= upgradePrice { bits -= upgradePrice bitsPerClick += 1 upgradePrice *= 1.5 } } static func bitClick() { bits += bitsPerClick } }
TodayViewController.swift
import UIKit import NotificationCenter class TodayViewController: UIViewController, NCWidgetProviding { @IBOutlet weak var bitCountLabel: UILabel! @IBOutlet weak var upgradeCostLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // 确保默认值已初始化(如果App启动时没调用,这里也可以补一次) AppUtility.setupDefaults() // 初始化标签显示 updateLabels() } // 点击加Bit @IBAction func clickBit(_ sender: Any) { AppUtility.bitClick() updateLabels() } // 点击升级 @IBAction func clickUpgrade(_ sender: Any) { AppUtility.upgradeClick() updateLabels() } // 统一更新标签的方法 private func updateLabels() { bitCountLabel.text = "\(AppUtility.bits)" upgradeCostLabel.text = "Cost \(AppUtility.upgradePrice)" } func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) { // 小组件更新时也同步标签 updateLabels() completionHandler(.newData) } }
额外注意事项
- 建议在
AppDelegate的didFinishLaunchingWithOptions里调用AppUtility.setupDefaults(),确保全局初始化一次。 - 小组件的UI更新要放在
widgetPerformUpdate里,确保小组件刷新时能拿到最新数据。 - 把标签的命名改成更清晰的
bitCountLabel、upgradeCostLabel,避免和变量名混淆。
内容的提问来源于stack exchange,提问作者Trgrit Tjforce
相关产品推荐
相关产品推荐

