I noticed when I create a @Model, and go to Xcode's Refactor->Generate memberwise initializer, Xcode does this:
@Model
class A {
internal init(a: String = "", b: Date = Date(), _$backingData: any BackingData<SchemaV2.A> = A.createBackingData()) {
self.a = a
self.b = b
self._$backingData = _$backingData
}
var a: String = ""
var b: Date = Date()
}
If I was doing it manually with my own initializer, I never used something like createBackingData()
. What is its purpose?
I encourage you to see what the @Model
macro actually expands to, by right clicking @Model
and select "Expand Macro". There are more macros in its expansion - try expanding those too. Note that these underscore-prefixed things are all very subject to change in the future.
One property that @Model
generates is
private var _$backingData: any SwiftData.BackingData<A> = A.createBackingData()
This explains why "generate memberwise initializer" generated an extra parameter. That's just how it treats instance var
s that are already initialised at its declaration. Try generating a memberwise initialiser for class Foo { private var x = 1 }
, and see the same thing happen consistently.
createBackingData
is a static method declared in the PersistentModel
protocol, which all @Model
s conform to. It is not documented, but we can roughly guess what it does from its name. It probably create something that actually stores the values of the persisted attributes of the model. BackingData
is likely a wrapper around an NSManagedObject
or something like that.
Since _$backingData
already has an initialiser expression, there is no need for your initialiser to initialise _$backingData
at all.
The values of the persisted attributes of the model don't actually live in the model class object. @Model
first add a @_PersistedProperty
macro to the stored properties you declare, then @_PersistedProperty
turns them into computed properties with these accessors:
@_PersistedProperty var name: String
// expands to:
var name: String
{
@storageRestrictions(accesses: _$backingData, initializes: _name)
init(initialValue) {
_$backingData.setValue(forKey: \.name, to: initialValue)
_name = _SwiftDataNoType()
}
get {
_$observationRegistrar.access(self, keyPath: \.name)
return self.getValue(forKey: \.name)
}
set {
_$observationRegistrar.withMutation(of: self, keyPath: \.name) {
self.setValue(forKey: \.name, to: newValue)
}
}
}
@Transient
private var _name: _SwiftDataNoType
It only leaves an underscore-prefixed stored property that is basically just a marker to ensure that you have initialised everything in the initialiser. Without it, you don't have to initialise any of the model attributes in the initialiser, since they all turned into computed properties.