I am getting the following purple warning in my SwiftUI view update:
"Publishing changes from within view updates is not allowed, this will cause undefined behavior."
The warning is caused due to the following in my ObservableObject
code:
fileprivate func set(_ value: Any?, forKey key: Key) {
if peerDeviceSettings != nil {
peerDeviceSettings?[key.rawValue] = value
} else {
UserDefaults.standard.set(value, forKey: key.rawValue)
}
objectWillChange.send() //<--- This line causes warning
}
var someProperty:Array<MyPropertyType> {
get {
var allValues:Array<MyPropertyType > = []
if let storedValues = setting(forKey: Key.myValues) as? Array<[String:Any]> {
allValues = storedValues.map { MyPropertyType($0) }
} else {
//Install default values now
let allValuesDictionary = MyPropertyType.defaultValuesArray()
set(allValuesDictionary, forKey: Key.myValues)
allValues = self.someProperty
}
return allValues
}
set (newValue) {
var dictionaries:Array<Dictionary<String, Any>> = []
dictionaries = newValue.map { $0.dictionary() }
set(dictionaries, forKey: Key.myValues)
}
}
So basically I am reading a setting property from body
var and the settings code is trying to install default values if nothing was found at the startup and the setter code is triggering objectWillChange.send()
. To solve it, I have modified the setter code to notify changes on the main queue.
fileprivate func set(_ value: Any?, forKey key: Key) {
if peerDeviceSettings != nil {
peerDeviceSettings?[key.rawValue] = value
} else {
UserDefaults.standard.set(value, forKey: key.rawValue)
}
DispatchQueue.main.async {
self.objectWillChange.send() //<-- This fixes the warning
}
}
I want to know if this solution has any pitfalls or there is a better solution?
Will it work? Yes.
Are there any pitfalls? Yes. You are setting a value in a get function, which breaks the Principle Of Least Astonishment.
Is there a better solution? Probably, depending on what you are trying to do.
As @jnpdx mentioned, you could use @AppStorage in the view itself. You could also give back a default value in the read function without setting the underlying value itself. You could set the default value on appear, or segregate this matter completely from the ViewModel, so that a provider of this collection is injected and subscribed to.
Ask yourself: who owns this model? At what stage should it be initiated with default values? Your code should reflect the answer to these questions.