swiftswiftui

Unable to initialize nil Date


I am having some trouble initializing the date. I use the date to tell when JSON data is updated. The app currently fails to update the JSON data when first run, but it works fine for all other updates. Since my JSON data is updated daily, I am trying to set the startup date to an old date. This will be caught as out-of-date, and will do an immediate update.

I looked into optional dates, but Apple always wants you to coalesce using ?? in case the optional fails, which could be in a half dozen different places in my code. It doesn't seem like very good coding practice to make up dates to satisfy the optionals. I suppose this may be a better way to do initialization since the date may be nil.

When I step through the app for the first time with a nil storedDate, I see the date set to the current date even before it hits the initializer. It never appears to hit the initializer with the old date. I have tried different versions of the initializer without success.

The second line of the initializer (let storedDate = UserDefaults...) gives the warning: "Initialization of immutable value 'storedDate' was never used; consider replacing with assignment to '_' or removing it." I don't know how to get rid of this message--I am using storedDate.

@Observable final class GetRatesModel {
    
    @ObservationIgnored var storedDate: Date = Date()
    
    init() {
        if UserDefaults.standard.object(forKey: StorageKeys.upd.rawValue) != nil {
            let storedDate = UserDefaults.standard.object(forKey: StorageKeys.upd.rawValue) as! Date
        } else {
            let storedDate = Date.now-10000000
            UserDefaults.standard.setValue(storedDate, forKey: StorageKeys.upd.rawValue)
        }
    
    @MainActor func checkForUpdates(baseCur: String) async throws {
        
        // dates match?
        if !Calendar.current.isDate(Date.now, equalTo: storedDate, toGranularity: .day) {
            
            print("getting data")
            
            // is the json data date different from the current data date?
            let stored8601 = storedDate.formatted(.iso8601.year().month().day().dateSeparator(.dash))
            if stored8601 != JsonDate {
    
            // use the new json data: update date
                UserDefaults.standard.setValue(Date.now, forKey: StorageKeys.upd.rawValue)

         . . .
struct ShowRatesComment: View {

    var g: GeometryProxy

    @Environment(GetRatesModel.self) private var vm
    
    var body: some View {
             
        let lastRateUpdate = vm.storedDate
                
        let updateDate = lastRateUpdate.formatted(Date.FormatStyle().year().month().day().hour().minute().timeZone())

        VStack(alignment: .leading) {
             if g.size.height > g.size.width {
                  Text("Updated: \(updateDate)")

         . . .

Solution

  • More code is more problems so I would suggest you simplify this by making use of @AppStorage instead to access UserDefaults and to do it directly in the view instead of the class.

    struct ShowRatesComment: View {
        var g: GeometryProxy
        @Environment(GetRatesModel.self) private var vm
        @AppStorage(StorageKeys.upd.rawValue) private var storedDate: Date = .distantPast
    
        //...
    }
    

    Note the use of the constant distantPast which is better than subtracting some magic number.

    Then I would remove the corresponding property from the class and instead pass the date to the function checkForUpdates

    @Observable final class GetRatesModel {
        init() {}
        
        @MainActor func checkForUpdates(baseCur: String, storedDate: Date) async throws {
            //...
        }
    }
    

    If you still want to have the date as a property in your class then you can use @AppStorage there as well so no need for that init code you have now

    @Observable final class GetRatesModel {
        @AppStorage(StorageKeys.upd.rawValue) private var storedDate: Date = .distantPast
        init() {}
        
        //...
    }