简体   繁体   中英

UserDefaults state not refresh

I use "UserDefaults" to store the date time of the user to reload the page. However, I found that it does not refresh even if the time passed

final class ModelData: ObservableObject {
    @Published var lastLaunchDate: Date = UserDefaults.standard.value(forKey: "lastLaunch") as? Date ?? Date()

    func getTimeStamp() -> Date {
                    
            let now_components = Calendar.current.dateComponents([.month, .day, .hour, .minute], from: Date())
            let lastLaunch_components = Calendar.current.dateComponents([.month, .day, .hour, .minute], from: lastLaunchDate)
            
            print(now_components.minute, lastLaunch_components.minute)
            
            if now_components.minute != lastLaunch_components.minute {
                lastLaunchDate = Date()
                UserDefaults.standard.setValue(lastLaunchDate, forKey: "lastLaunch")
            }
            print(now_components.minute, lastLaunch_components.minute)
        }
        return lastLaunchDate
    }
}

Since I am testing, I use 'minute' to ease my test. I expect that when there is change of minute, the above piece of code will update my userDefaults.

struct HomeView: View {

    @EnvironmentObject var modelData: ModelData
    
    var body: some View {
        Text("\(modelData.getTimeStamp())")
    }
}

I found that I browse into another page, go back to HomeView. The timestamp did not get update at all. I printed the lastLaunchDate, it also did not update as well.

I found that I browse into another page, go back to HomeView. The timestamp did not get update at all.

If you navigate back to a parent View , SwiftUI does not re-render it unless something changes.
Instead... you will need to use the .onAppear view modifier to call your function.

Also, Text("\(modelData.getTimeStamp())") is not very SwiftUI especially in your case.
Since you have @Published var lastLaunchDate , use it as it's meant to be used:

Text("\(modelData.lastLaunchDate)")

Now when lastLaunchDate is changed, the SwiftUI engine will automatically update the View .

Solution:

final class ModelData: ObservableObject {
    @Published var lastLaunchDate = UserDefaults.standard.value(forKey: "lastLaunch") as? Date ?? Date()
    
    func refresh() {
        let compareDate = Date()
        let refreshInterval = TimeInterval(3) //3 second interval for test purpose
        
        if compareDate.timeIntervalSince(lastLaunchDate) > refreshInterval {
            lastLaunchDate = compareDate
            UserDefaults.standard.setValue(lastLaunchDate, forKey: "lastLaunch")
        }
    }
}

struct ContentView: View {
    @EnvironmentObject var modelData: ModelData
    
    var body: some View {
        NavigationView {
            VStack {
                Text("\(modelData.lastLaunchDate)")
                NavigationLink(destination: Text("Destination"),
                               label: { Text("Navigate") })
            }
            .onAppear {
                modelData.refresh()
            }
        }
    }
}

NOTE:
if now_components.minute.= lastLaunch_components.minute is error prone.
Example: compare last-update-time of 00:01 with current-time of 01:01.
Check will fail.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM