How can I force a function only runs once during the whole app lifecycle?

Hi, I want to generate some sample datas for demonstrating the functions of my app when the app launched. My codes are as followings:

 func generateSampleData() {
            let hasLaunchedKey = "HasLaunchedBefore"
            let defaults = UserDefaults.standard
            if !defaults.bool(forKey: hasLaunchedKey) {
            //generate the demo data
            }
                defaults.set(true, forKey: hasLaunchedKey)
            }

And I put the func in a view's onAppear modifier. I found every time I go the view, it generates the demo data again, which results in producing a lot of demo data. But I have set the status of the function running in the userdefault. Why did it happen?

Best Wishes,

I found the func can stop after about 5 minutes. I think it's the time that the key is saved in UserDefaults. But it's too long. Is there another method to let the function run only once?

I think it may be because your code doesn't synchronize() the change to the defaults, so it doesn't save it immediately.

You could add a call to defaults.synchronize(), but you'd still be running this code every time the view is drawn (in .onAppear). That's a code smell; you're doing it in a place that can be executed multiple times within the app's lifetime, which opens you up to exactly the sort of behaviour you're experiencing.

A better way to do this is to move it to the main app startup, e.g.:

@main
struct MainApp: App {
  init() {
    // Is this the first launch of the app?
    if(defaultsGetIsFirstLaunch(version: 6)) {
      // Generate sample data...
      defaultsUpdateIsFirstLaunch(version: 6, value: false)
    }
  }
}

func defaultsGetIsFirstLaunch(version: Int) -> Bool {
	defaultsGetBool(forKey: "firstLaunchVersion\(version)", withFallback: true)
}

func defaultsUpdateIsFirstLaunch(version: Int, value: Bool) {
	defaultsSet(value as Bool, forKey: "firstLaunchVersion\(version)")
}

func defaultsGetBool(forKey key: String, withFallback fallback: Bool) -> Bool {
	guard let value = UserDefaults(suiteName: "myappgroup")?.object(forKey: key) else { return fallback }
	return value as? Bool ?? fallback
}

func defaultsSet(_ value: Any, forKey key: String) {
	UserDefaults(suiteName: "myappgroup")?.set(value, forKey: key)
	UserDefaults(suiteName: "myappgroup")?.synchronize()
}

Note: I abstract the defaults functions out to their own methods, as above, so I have a method that gets a Bool from the defaults, one for an Int, one for a String etc., and a single method that sets a value in the defaults.

The get functions take a 'fallback' value, so if the value hasn't been set yet, it will return the fallback value instead.

So... In the code above, it runs in the main app's init() function. It checks if this is the first launch and passes in true if the key hasn't been set yet. It hasn't, so it returns true - this is the first launch of version 6. We generate the sample data, then we set the defaults value to false. The next time you run the app, the key has been set to false, so the code doesn't run anymore.

How can I force a function only runs once during the whole app lifecycle?
 
 
Q