I’ve an app that at the moment makes use of @AppStorage to retailer consumer settings. Quite a lot of settings ought to sync between gadgets so I added a generic mannequin to retailer consumer settings in SwiftData like so:
@Mannequin
class CloudSetting {
var key: SettingItem
var date: Date?
var worth: Knowledge
init(key: SettingItem, date: Date?, worth: T) {
self.key = key
self.date = date
self.worth = attempt! JSONEncoder().encode(worth)
}
func getValue(as sort: T.Sort) -> T? {
attempt? JSONDecoder().decode(sort, from: worth)
}
func setValue(_ newValue: T) {
self.worth = attempt! JSONEncoder().encode(newValue)
}
}
I already had an enum (SettingItem) that comprises all related settings that’s then used to arrange the defaults within the newly @Observable class “SettingsManager” that handles the creation and adjustments to the mannequin information. For this I created an exemplary property “useLiveActivity” to make accessing the setting within the view simpler:
@Observable
class SettingsManager {
personal var modelContext: ModelContext
personal var settings = [SettingItem: [CloudSetting]]()
// Computed variables for simpler entry
var useLiveActivity: Bool {
get { getSetting(for: .useliveActivity)}
set { setSetting(for: .useliveActivity, to: newValue)}
}
init(modelContext: ModelContext) {
self.modelContext = modelContext
fetchAndCreateSettings()
}
func getSetting(for key: SettingItem) -> T {
(settings[key]?.first?.getValue(as: T.self))!
}
func setSetting(for key: SettingItem, to worth: Codable) {
settings[key]?.first?.setValue(worth)
}
func fetchAndCreateSettings() {
// Fetch present settings or if empty, create a brand new array occasion
var existingSettings = (attempt? modelContext.fetch(FetchDescriptor())) ?? [CloudSetting]()
for setting in SettingItem.allCases {
// Test that setting will not be but saved in container, in any other case proceed with subsequent iteration
guard (existingSettings.firstIndex(the place: {$0.key == setting}) == nil) else {
proceed
}
// Add new setting to array and to container
let setting = CloudSetting(key: setting, date: setting.defaultDate, worth: setting.defaultValue)
modelContext.insert(setting)
existingSettings.append(setting)
}
// Save after inserting obligatory parts
attempt? modelContext.save()
// Replace variable
settings = Dictionary(grouping: existingSettings, by: { $0.key })
}
The SettingsManager is then instantiated within the App Struct like so:
struct ArbeitszeitApp: App {
/// Container holds the persistent modelContainer for all SwiftData
@State personal var container: ModelContainer
/// SettingsManager is a retailer for all settings that must be synced through CloudKit (SwiftData)
@State personal var settingsManager: SettingsManager
init() {
let container = ArbeitszeitApp.createContainer()
_container = State(wrappedValue: container)
let settingsManager = SettingsManager(modelContext: container.mainContext)
_settingsManager = State(wrappedValue: settingsManager)
}
var physique: some Scene {
WindowGroup {
ContentView()
.setting(settingsManager)
}
.modelContainer(container)
}
}
Then I can entry the information within the view like so and whereas this typically works as anticipated, I used to be questioning what could be one of the best strategy:
struct ManageLiveActivitiesView: View {
@Surroundings(SettingsManager.self) personal var settingsManager
@Bindable var settings: SettingsManager
var physique: some View {
Checklist {
Toggle("", isOn: Binding(
get: {settingsManager.stay},
set: {settingsManager.stay = $0})
Toggle("", isOn: $settingsManager.stay)
}
}
}
}
So the principle query(s) could be:
- Is it extra widespread/higher to
- use the SettingsManager occasion from the @Surroundings within the view on to entry and alter the setting however with the necessity to create an specific binding
- or learn the SettingsManager occasion within the root view of the settings after which move it all the way down to the views to make dealing with the binding simpler?
- Does it make sense to create helper variables for all settings within the SettingsManager for simpler entry within the view?
- Or is there a greater strategy on the whole?