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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:40:00