Saving Date/Time to Userdefaults Through Didset on @Published Var (From Timepicker Component of Swiftui)

Saving Date/Time to UserDefaults through didSet on @Published var (from TimePicker component of SwiftUI)

The issue with how you store and annotate your view model. You should never be creating an @ObservedObject in the View itself, but rather injecting it. Whenever an @ObservedObject's @Published property changes, the View which stores that object will be reloaded - this means that if you are initialising that @ObservedObject inside the View, a new instance of that object will be created.

You need to inject the object into your view to avoid recreating it every time your view is refreshed.

struct SettingsMain: View {
@ObservedObject var userSettings: UserSettings
@State private var showTimepickerEvening = false
...

And from wherever you create SettingsMain (let's call it MainView as an illustration), create UserSettings there and annotate it as @State (or if you are creating it from something other than a View - say a ViewModel, then make it @Published):

struct MainView: View {
@State var userSettings = UserSettings()

var body: some View {
SettingsMain(userSettings: userSettings)
}
}

Run code when date in DatePicker is changed in SwiftUI didSet function?

My question is, when I'm setting the selectedDate value in the DatePicker, why is there nothing printed to the console?

For now didSet does not work for @State property wrapper.

Here is an approach to have side effect on DatePicker selection changed

struct ViewName: View {
@State private var selectedDay = Date()

var body: some View {
let dateBinding = Binding(
get: { self.selectedDay },
set: {
print("Old value was \(self.selectedDay) and new date is \($0)")
self.selectedDay = $0
}
)
return VStack {
DatePicker(selection: dateBinding, in: Date()..., displayedComponents: .date) { Text("") }
}
}
}

UserDefaults insanity, take 2 with a DatePicker

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
}

}

SwiftUI Initialise variables that will be stored in UserDefaults

true in your example above is the value to fall back on if there is nothing in user defaults. If there is something stored in user defaults for "pullData", it will be used.

If you didn't want to use a fallback value then you'd have to have the property as a Bool? instead of a Bool:

@AppStorage("pullData") var pullData: Bool?

But optional booleans are a terrible idea, so don't do that.

Detect when user taps out of or saves on DatePicker

You could try rolling your own DatePickerView, such as this approach:

struct ContentView: View {
@State private var birthdate = Date()

var body: some View {
DatePickerView(date: $birthdate)
}
}

struct DatePickerView: View {
@Binding var date: Date
@State var hasChanged = false

var body: some View {
ZStack {
Color.white.opacity(0.01).ignoresSafeArea()
.onTapGesture {
if hasChanged {
print("---> do a network call now with: \(date)")
hasChanged = false
}
}
DatePicker("Birth Date", selection: $date, displayedComponents: .hourAndMinute)
.pickerStyle(.wheel)
.onChange(of: date) { val in
hasChanged = true
}
}
}
}


Related Topics



Leave a reply



Submit