ObservableObject with DatePicker in SwiftUI

Viewed 98

Can you help me with the following source code:

import SwiftUI


class DateHandler {
    
    /* create a variable
     Class to manage start date and end date
    */
    

    var date1 : Date {
        didSet{
            print("StartDate: \(date1)")
        }
    }
    var date2 : Date {
        didSet{
            print("EndDate: \(date2)")
        }
    }
    
    var startDate:Date {
        get{
            return date1
        }
        
        set(newValue){
            date1 = toLocalTime(date: newValue)
        }
        
    }
    
    var endDate:Date {
        get{
            return date2
        }
        
        set(newValue){
            date2 = addOneDay(date: toLocalTime(date: newValue))
        }
        
    }
    
    init(){
        self.date1 = Date()
        self.date2 = Date()
    }
    
    
    //Customize time zone
    func toLocalTime(date : Date) -> Date {
    
        
        //Auswahl der aktuellen Kalender
        let calendar = Calendar.current
        
        //Auswahl der Zeitzone
        let timezone = TimeZone.current
        
        //Bestimmen Anzahl Sekunden zwischen Zeitzone und GMT
        let seconds = TimeInterval(timezone.secondsFromGMT(for: date))
        
        //Anpassen des eingelesenen Werts
        let newDate = calendar.date(bySettingHour: 00, minute: 00, second: 00, of: date)
        let dateLocalTimezone = Date(timeInterval: seconds, since: newDate!)

        return dateLocalTimezone
    }
    
    func addOneDay(date : Date) -> Date {

        let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: date)
        return tomorrow!
    }

}

//Class to use DateHandler() as a ObservableObject
class DateHandlerHelper : ObservableObject{
    
    @Published var dateHandler = DateHandler()
    
}



struct ContentView: View {
    
    @ObservedObject var dateHandlerHelper = DateHandlerHelper()
    
    @State var startDateHelper = Date()
    @State var endDateHelper = Date()
    

    var body: some View {
        
        DatePicker("Date1", selection: $startDateHelper)
            .onAppear(perform: {
                print("onAppear")
                dateHandlerHelper.dateHandler.startDate = startDateHelper
                print(dateHandlerHelper.dateHandler.startDate)
                
            })
            .onChange(of: startDateHelper) { newValue in
                
                print("onChange")
                
                dateHandlerHelper.dateHandler.startDate = newValue
                
                if newValue > endDateHelper{
                    print("Correction Date 1")
                    startDateHelper = endDateHelper
                    dateHandlerHelper.dateHandler.startDate = endDateHelper
                    
                    
                }
                

                print(dateHandlerHelper.dateHandler.startDate)
            }
        
        
        DatePicker("Date2", selection: $endDateHelper)
            .onAppear(perform: {
                
                dateHandlerHelper.dateHandler.endDate = endDateHelper
                print(dateHandlerHelper.dateHandler.endDate)
            })
            .onChange(of: endDateHelper) { newValue in
                print("onChange")
                dateHandlerHelper.dateHandler.endDate = newValue
                print(dateHandlerHelper.dateHandler.endDate)
            }
                
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

I have a view with two DatePickers to select a start date and an end date. For the start date and end date there is the class DateHandler.

I want to handle the DateHandler class as an ObservableObject. Because of the getter/setter function used, I can only address the DateHandler class indirectly through the DateHandlerHelper class. Is there a better/simpler way here?

If I address the variables dateHandlerHelper.dateHandler.startDate and dateHandlerHelper.dateHandler.endDate directly via the DatePickers, I can change the values, but the DatePickers themselves are not updated. For this reason I have introduced the variables startDateHelper and endDateHelper in the view. Do you have any idea how I can do this differently to save me the variables?

2 Answers

I don't see why you need to do those date calculations every time instead I changed so that they are done once when the DateHelper class is initialized.

class DateHandler: ObservableObject {
    @Published var startDate: Date
    @Published var endDate: Date

    init(){
        startDate = Self.toLocalTime(date: .now)
        //I assume endDate also should be adjusted for local time?
        let date = Self.addOneDay(date: .now) 
        endDate = Self.toLocalTime(date: date)
    }

    private static func toLocalTime(date : Date) -> Date {
        // removed for brevity
    }

    private static func addOneDay(date : Date) -> Date {
        // removed for brevity
    }
}

And then the view would be much simpler

struct ContentView: View {
    @ObservedObject var dateHandler = DateHandler()

    var body: some View {
        VStack {
            DatePicker("Date1", selection: $dateHandler.startDate)
            DatePicker("Date2", selection: $dateHandler.endDate)
        }
    }
}

This seems to be the right way:

class DateModel : ObservableObject{
    
    var date1 : Date{
        didSet{
            print(date1)
        }
    }
    var date2 : Date{
        didSet{
            print(date2)
        }
    }
    
    var startDate:Date {
        get{
            return date1
        }
        
        set(newValue){
        
            date1 = toLocalTime(date: newValue)
            startDateString = date1.toString()
            print(startDateString)
                
        }
        
    }
 
    
    var endDate:Date {
        get{
            return date2
        }
        
        set(newValue){
            date2 = toLocalTime(date: newValue)
        }
        
    }
    
    init(){
        self.date1 = Date()
        self.date2 = Date()
    }

    
    
    //Customize time zone
    func toLocalTime(date : Date) -> Date {
    
        //Selection of the current calendar
        let calendar = Calendar.current
        
        //Time zone selection
        let timezone = TimeZone.current
        
        //Determine number of seconds between time zone and GMT
        let seconds = TimeInterval(timezone.secondsFromGMT(for: date))
        
        //Adjusting the input value
        let newDate = calendar.date(bySettingHour: 00, minute: 00, second: 00, of: date)
        let dateLocalTimezone = Date(timeInterval: seconds, since: newDate!)

        return dateLocalTimezone
    }
    
}

struct ContentView: View {
    
    @ObservedObject var dateModel = DateModel()
    
    var dateFormatter: DateFormatter {
        let formatter = DateFormatter()
        formatter.dateStyle = .long
        return formatter
    }
    
    var body: some View {
        
        Form{
            Section{
                
                DatePicker("start date", selection: $dateModel.startDate, displayedComponents: [.date]).onChange(of: dateModel.startDate) { newValue in
                    print(newValue.toString())
                }
                
                DatePicker("final date", selection: $dateModel.endDate, displayedComponents: [.date])
                

                

            }
            
        }
                
    }
}
Related