Схожу с ума по UserDefaults в Swift [UI] - PullRequest
0 голосов
/ 17 апреля 2020

Запуская себя в Swift и SwiftUI, я нахожу процесс перехода с UIKit довольно сложным. В настоящее время растоптаны UserDefaults, даже после попытки разобраться во многих учебных пособиях, которые я нашел в Интернете.

Пожалуйста, скажите мне, что я делаю здесь неправильно: ОЧЕНЬ простой код:

  1. зарегистрировать значение bool для UserDefault,
  2. отобразить это bool в тексте!

Не проще, чем это. Но я не могу заставить его работать, так как вызов UserDefaults выдает это сообщение об ошибке:

Метод экземпляра 'appendInterpolation' требует, чтобы 'Bool' соответствовал '_FormatSpecifiable'

Мое "приложение" является приложением по умолчанию для одного представления со следующими 2 изменениями:

1- В AppDelegate я регистрирую свой bool:

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, I попробуйте отобразить его (внутри struct ContentView: View):

let defaults = UserDefaults.standard

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

Есть идеи?

Спасибо

Ответы [ 7 ]

1 голос
/ 17 апреля 2020

Чтобы решить вашу проблему, просто добавьте описание переменной, например:

var body: some View {
    Text("The BOOL 1 value is : Bool 1 = \(defaults.bool(forKey: "MyBool 1").description)")
}
1 голос
/ 18 апреля 2020

Чтобы ответить на ваши вопросы:

1 - зарегистрируйте значение bool в UserDefault,

2 - отобразите это bool в тексте!

Я протестировал следующий код и подтвердите, что он работает на ios 13.4 и macos с использованием катализатора. Обратите внимание на перенос строки (...).

в классе 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
}

в ContentView

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) ")
}
}
1 голос
/ 17 апреля 2020

Ваша проблема в том, что инициализатор 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 для поддержки bools, и тогда ваш оригинальный код должен работать:

extension LocalizedStringKey.StringInterpolation {
    mutating func appendInterpolation(_ value: Bool) {
        appendInterpolation(String(value))
    }
}
0 голосов
/ 17 апреля 2020

Я понял, что лучший способ использовать UserDefaults - внутри класса. Это помогает нам подписаться на этот класс из любой модели, используя оболочку свойства @ObservedObject.

Логический метод можно использовать для остальных типов

//
//  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
    }
}

Примечание: Этот код не проверен. Прямо здесь напечатано.

0 голосов
/ 17 апреля 2020

Попробуйте это:

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)
    }
}
0 голосов
/ 17 апреля 2020

в SwiftUI я использую эти:

UserDefaults.standard.set(true, forKey: "MyBool 1")
let bull = UserDefaults.standard.bool(forKey: "MyBool 1")
0 голосов
/ 17 апреля 2020

Не уверен, почему вы используете register, но вы можете просто установить значение bool следующим образом:

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