UserDefaults insanity, take 2 with a DatePicker

只谈情不闲聊 提交于 2020-04-30 09:18:00

问题


Progressing on UserDefaults insanity in SwiftUI, after a previous post on basic UserDefaults which basically exposed the need to use a String() wrapper around UserDefaults values...

I am now stomped by the data flow :

The idea is to present a DatePicker, set to a UserDefaults value registered in AppDelegate on first launch. Subsequently, the user picks another date that is set to the UserDefaults. But every time I launch the app after having "killed" it (i.e swiped up from app switcher), the Picker displays the present date and NOT the one last saved in UserDefaults.

Also, I display some texts above and below the picker to try and make sense of the data flow, but it seems that there is a one step lag in the displaying of the dates, if anyone has the time to give it a try, here is the code :

1- In AppDelegate, I register my initial UserDefaults (like I always did in UIKit) :

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

        return true
    }

2- in ContentView, I try to display them :

import SwiftUI

struct ContentView: View {

    let defaults = UserDefaults.standard

    var dateFormatter: DateFormatter {
     let formatter = DateFormatter()
    // formatter.dateStyle = .long
     formatter.dateFormat = "dd MMM yy"
     return formatter
     }

    @State var selectedDate = Date()

    init() {
        self.loadData() // This should set "selectedDate" to the UserDefaults value
    }

    var body: some View {

        VStack {
            Text("The BOOL 1 value is : Bool 1 = \(String(defaults.bool(forKey: "MyBool 1")))")
            Divider()
            Text("My string says : \(String(defaults.string(forKey: "MyString")!))")
            Divider()
            Text("The date from UserDefaults is : \(dateFormatter.string(from: defaults.object(forKey: "MyDate") as! Date))")
            Divider()
            DatePicker(selection: $selectedDate, label: { Text("") })
                .labelsHidden()
                .onReceive([self.selectedDate].publisher.first()) { (value) in
                    self.saveDate()
                }

            Divider()
            Text("The chosen date is : \(selectedDate)")
        }
    }

    func loadData() {
        selectedDate = defaults.object(forKey: "MyDate") as! Date
        print("----> selected date in \"init\" from UserDefaults: \(dateFormatter.string(from: selectedDate) )) ")
    }

    private func saveDate() { // This func is called whenever the Picker sends out a value
        UserDefaults.standard.set(selectedDate, forKey: "MyDate")
        print("Saving the date to User Defaults : \(dateFormatter.string(from: selectedDate) ) ")
    }

}

Any help would be appreciated !


回答1:


here is my 2nd answer, if you want to update the text also...it is not "nice" and for sure not the best way, but it works (i have tested it)

struct ContentView: View {

    let defaults = UserDefaults.standard

    var dateFormatter: DateFormatter {
     let formatter = DateFormatter()
    // formatter.dateStyle = .long
     formatter.dateFormat = "dd MMM yy"
     return formatter
     }
    @State var uiUpdate : Int = 0
    @State var selectedDate : Date
    @State var oldDate : Date = Date()

    init() {
        _selectedDate = State(initialValue: UserDefaults.standard.object(forKey: "MyDate") as! Date) // This should set "selectedDate" to the UserDefaults value
    }

    var body: some View {

        VStack {
            Text("The BOOL 1 value is : Bool 1 = \(String(defaults.bool(forKey: "MyBool 1")))")
            Divider()
            Text("My string says : \(String(defaults.string(forKey: "MyString")!))")
            Divider()
            Text("The date from UserDefaults is : \(dateFormatter.string(from: defaults.object(forKey: "MyDate") as! Date))")
                .background(uiUpdate < 5 ? Color.yellow : Color.orange)
            Divider()
            DatePicker(selection: $selectedDate, label: { Text("") })
                .labelsHidden()
                .onReceive([self.selectedDate].publisher.first()) { (value) in
                    if self.oldDate != value {
                        self.oldDate = value
                        self.saveDate()
                    }
                }

            Divider()
            Text("The chosen date is : \(selectedDate)")
        }
    }

    func loadData() {
        selectedDate = defaults.object(forKey: "MyDate") as! Date
        print("----> selected date in \"init\" from UserDefaults: \(dateFormatter.string(from: selectedDate) )) ")
    }

    private func saveDate() { // This func is called whenever the Picker sends out a value
        UserDefaults.standard.set(selectedDate, forKey: "MyDate")
        print("Saving the date to User Defaults : \(dateFormatter.string(from: selectedDate) ) ")
        uiUpdate = uiUpdate + 1
    }

}



回答2:


try this:

struct ContentView: View {

    let defaults = UserDefaults.standard

    var dateFormatter: DateFormatter {
     let formatter = DateFormatter()
    // formatter.dateStyle = .long
     formatter.dateFormat = "dd MMM yy"
     return formatter
     }

    @State var selectedDate : Date

    init() {
        _selectedDate = State(initialValue: UserDefaults.standard.object(forKey: "MyDate") as! Date) // This should set "selectedDate" to the UserDefaults value
    }

    var body: some View {

        VStack {
            Text("The BOOL 1 value is : Bool 1 = \(String(defaults.bool(forKey: "MyBool 1")))")
            Divider()
            Text("My string says : \(String(defaults.string(forKey: "MyString")!))")
            Divider()
            Text("The date from UserDefaults is : \(dateFormatter.string(from: defaults.object(forKey: "MyDate") as! Date))")
            Divider()
            DatePicker(selection: $selectedDate, label: { Text("") })
                .labelsHidden()
                .onReceive([self.selectedDate].publisher.first()) { (value) in
                    self.saveDate()
                }

            Divider()
            Text("The chosen date is : \(selectedDate)")
        }
    }

    func loadData() {
        selectedDate = defaults.object(forKey: "MyDate") as! Date
        print("----> selected date in \"init\" from UserDefaults: \(dateFormatter.string(from: selectedDate) )) ")
    }

    private func saveDate() { // This func is called whenever the Picker sends out a value
        UserDefaults.standard.set(selectedDate, forKey: "MyDate")
        print("Saving the date to User Defaults : \(dateFormatter.string(from: selectedDate) ) ")
    }

}



回答3:


Every time you start the App, it re-register the defaults. You could use this:

    if !UserDefaults.standard.bool(forKey: "first time only") {
        UserDefaults.standard.register(defaults: [
            "first time only": true,
            "MyBool 1": true,
            "MyString": "Soo",
            "MyDate": Date()
        ])
    }


来源:https://stackoverflow.com/questions/61285454/userdefaults-insanity-take-2-with-a-datepicker

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!