在 Swift[UI] 中疯狂使用 UserDefaults

Going crazy with UserDefaults in Swift[UI]

开始使用 Swift 和 SwiftUI,我发现从 UIKit 迁移的过程非常困难。 目前被 UserDefaults 所困扰,即使在尝试理解我在网上找到的许多教程之后也是如此。

请告诉我我做错了什么: 非常简单的代码:

  1. 向 UserDefault 注册一个布尔值,
  2. 在文本中显示该布尔值!

没有比这更简单的了。 但我无法让它工作,因为调用 UserDefaults 会抛出此错误消息:

Instance method 'appendInterpolation' requires that 'Bool' conform to '_FormatSpecifiable'

我的 "app" 是默认的单视图应用程序,具有以下 2 个更改:

1- 在 AppDelegate 中,我注册了我的布尔值:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    UserDefaults.standard.register(defaults: [
    "MyBool 1": true
    ])


    return true
}

2- 在 ContentView 中,我尝试显示它(在 struct ContentView: View 中):

let defaults = UserDefaults.standard

var body: some View {
    Text("The BOOL 1 value is : Bool 1 = \(defaults.bool(forKey: "MyBool 1"))")
}

有什么想法吗?

谢谢

不确定为什么要使用 register,但您可以像这样设置 bool 值:

UserDefaults.standard.set(true, forKey: "MyBool1")

在 SwiftUI 中我使用这些:

UserDefaults.standard.set(true, forKey: "MyBool 1")
let bull = UserDefaults.standard.bool(forKey: "MyBool 1")

试试这个:

struct MyView {
    private let userDefaults: UserDefaults

    // Allow for dependency injection, should probably be some protocol instead of `UserDefaults` right away
    public init(userDefaults: UserDefaults = .standard) {
        self.userDefaults = userDefaults
    }
}

// MARK: - View
extension MyView: View {

    var body: some View {
        Text("The BOOL 1 value is: \(self.descriptionOfMyBool1)")
    }
}

private extension MyView {
    var descriptionOfMyBool1: String {
        let key = "MyBool 1"
        return "\(boolFromDefaults(key: key))"
    }

    // should probably not be here... move to some KeyValue protocol type, that you use instead of `UserDefaults`...
    func boolFromDefaults(key: String) -> Bool {
        userDefaults.bool(forKey: key)
    }
}

你的问题是 Text(...) 初始值设定项采用 LocalizedStringKey 而不是 String ,后者在其字符串插值中支持与普通字符串不同的类型(不包括 Bool 显然)。

有几种方法可以解决这个问题。

您可以使用 Text 初始化器,它接受 String 并逐字显示它而不尝试进行任何本地化:

var body: some View {
    Text(verbatim: "The BOOL 1 value is : Bool 1 = \(defaults.bool(forKey: "MyBool 1"))")
}

或者,您可以扩展 LocalizedStringKey.StringInterpolation 以支持布尔值,然后您的原始代码应该可以工作:

extension LocalizedStringKey.StringInterpolation {
    mutating func appendInterpolation(_ value: Bool) {
        appendInterpolation(String(value))
    }
}

要解决您的问题,只需添加描述变量,例如:

var body: some View {
    Text("The BOOL 1 value is : Bool 1 = \(defaults.bool(forKey: "MyBool 1").description)")
}

我认为使用 UserDefaults 的最佳方式是在 class 中。它帮助我们使用 @ObservedObject 属性 包装器从任何模型订阅 class。

布尔方法可用于 types

的其余部分
//
//  ContentView.swift
//

import SwiftUI

struct ContentView: View {
    @ObservedObject var data = UserData()
    var body: some View {
        VStack {
            Toggle(isOn: $data.isLocked){ Text("Locked") }
            List(data.users) { user in
                Text(user.name)
                if data.isLocked {
                    Text("User is Locked")
                } else {
                    Text("User is Unlocked")
                }
            }
        }
    }
}


//
//  Model.swift
//

import SwiftUI
import Combine

let defaults = UserDefaults.standard
let usersData: [User] = loadJSON("Users.json")
// This is custom JSON loader and User struct is to be defined

final class UserData: ObservableObject {

    // Saving a Boolean

    @Published var isLocked = defaults.bool(forKey: "Locked") {
        didSet {
            defaults.set(self.isLocked, forKey: "Locked")
        }
    }

    // Saving Object after encoding

    @Published var users: [User] {
        // didSet will only work if used as Binding variable. Else need to create a save method, which same as the following didSet code.
        didSet {
            // Encoding Data to UserDefault if value of user data change
            if let encoded = try? JSONEncoder().encode(users) {
                defaults.set(encoded, forKey: "Users")
            }
        }
    }
    init() {
        // Decoding Data from UserDefault
        if let users = defaults.data(forKey: "Users") {
            if let decoded = try? JSONDecoder().decode([User].self, from: users) {
                self.users = decoded
                return
            }
        }
        // Fallback value if key "Users" is not found
        self.users = usersData
    }
    // resetting UserDefaults to initial values
    func resetData() {
        defaults.removeObject(forKey: "Users")
        self.isLocked = false
        self.users = usersData
    }
}

注意:此代码未经测试。这里直接打字

回答您的问题:

1- 将布尔值注册到 UserDefault,

2- 在文本中显示布尔值!

我测试了以下代码并确认它可以在 ios 13.4 和使用催化剂的 macos 上运行。注意 String(...) 包装。

在 class AppDelegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
//    UserDefaults.standard.register(defaults: ["MyBool 1": true])
   UserDefaults.standard.set(true, forKey: "MyBool 1")
    return true
}

在内容视图中

import SwiftUI

struct ContentView: View {

@State var defaultValue = false   // for testing
let defaults = UserDefaults.standard

var body: some View {
    VStack {
        Text("bull = \(String(UserDefaults.standard.bool(forKey: "MyBool 1")))")
        Text(" A The BOOL 1 value is Bool 1 = \(String(defaultValue))")
        Text(" B The BOOL 1 value is : Bool 1 = \(String(defaults.bool(forKey: "MyBool 1")))")
    }
    .onAppear(perform: loadData)
}

func loadData() {
    defaultValue = defaults.bool(forKey: "MyBool 1")
    print("----> defaultValue: \(defaultValue) ")
}
}