Persisting User Settings with SwiftData

I was wondering what the recommended way is to persist user settings with SwiftData?

It seems the SwiftData API is focused around querying for multiple objects, but what if you just want one UserSettings object that is persisted across devices say for example to store the user's age or sorting preferences.

Do we just create one object and then query for it or is there a better way of doing this?

Right now I am just creating:

import SwiftData

@Model
final class UserSettings {
   var age: Int = 0
   var sortAtoZ: Bool = true

   init(age: Int = 0, sortAtoZ: Bool = true) {
        self.age = age
        self.sortAtoZ = sortAtoZ
    }
}

In my view I am doing as follows:

import SwiftUI
import SwiftData

struct SettingsView: View {
   @Environment(\.modelContext) var context
   @Query var settings: [UserSettings]

   var body: some View {
      ForEach(settings) { setting in 
          let bSetting = Bindable(setting)
          Toggle("Sort A-Z", isOn: bSetting.sortAtoZ)
          TextField("Age", value: bSetting.age, format: .number)
      }
      .onAppear {
         if settings.isEmpty {
             context.insert(UserSettings(age: 0, sortAtoZ: true))
          }
      }
   }
}

Unfortunately, there are two issues with this approach:

  1. I am having to fetch multiple items when I only ever want one.
  2. Sometimes when running on a new device it will create a second UserSettings while it is waiting for the original one to sync from CloudKit.

AppStorage is not an option here as I am looking to persist for the user across devices and use CloudKit syncing.

Thank you for your post. After reviewing it, I have a suggestion for using UserDefaults instead, in my modest opinion and without knowing all your requirements.

If you intend to utilize SwiftData for this purpose, I recommend querying for the single instance of the model and binding it to your view. To achieve this, query for a single instance where a unique attribute, such as a stored value, matches a specific value. Retrieve the stored ID and use it in your filter to ensure that only one instance of the model is fetched and used within the view.

To facilitate future debugging, you may also consider deleting the existing model from CloudKit to eliminate any potential duplicates in your container. This can be done from the CloudKit Dashboard.

While UserDefaults may not be the most efficient solution for your specific requirements, I would be interested in understanding the reasons behind your decision to use SwiftData. Additionally, I would appreciate any recommendations from other developers regarding this matter.

Albert Pascual
  Worldwide Developer Relations.

I would wrap the UserSettings type inside an observable class and then use that class as an environment value

@Environment(SettingsManager.self) var settingsManager

Then this manager object could be made to handle all loading, updating and saving of the UserSettings object internally to have only one single source of truth.

Persisting User Settings with SwiftData
 
 
Q