This is the same for me on M1 Ultra MacBook Pro.
Here is an example of one of my most simple model class before macro expansion :
//
// LibraryObject.swift
// LAFoundation
//
// Created by Cyril Anger on 03/07/2019.
// Copyright © 2019 LyricApps. All rights reserved.
//
import SwiftUI
import LAFoundation
import LAGraphics
public extension String.LocalizationTable {
static let libraryObject = Self("LibraryObject", bundle: .module)
}
@DatabaseModel(localizationTable: .libraryObject)
open class LibraryObject: DatabaseObject {
open class var defaultName: String {
fatalError()
}
@Attribute([.validation], defaultValue: "Self.defaultName")
open var name: String
private func validateName(_ name: String) -> String {
return name.validPathIdentifier
}
open var defaultIcon: LAImage? {
return nil
}
@Attribute([.referenceComparison])
@Image(maxSize: CGSize(width: 32, height: 32), compareMaxSize: CGSize(width: 24, height: 24))
open var icon: LAImage?
open var isHidden = false
// MARK: Init
override public init(databaseManager: DatabaseManager, uniqueID: String = UUID().uuidString) {
name = Self.defaultName
super.init(databaseManager: databaseManager, uniqueID: uniqueID)
}
}
And after expansion :
//
// LibraryObject.swift
// LAFoundation
//
// Created by Cyril Anger on 03/07/2019.
// Copyright © 2019 LyricApps. All rights reserved.
//
import SwiftUI
import LAFoundation
import LAGraphics
public extension String.LocalizationTable {
static let libraryObject = Self("LibraryObject", bundle: .module)
}
@DatabaseModel(localizationTable: .libraryObject)
open class LibraryObject: DatabaseObject {
open class var defaultName: String {
fatalError()
}
@Attribute([.validation], defaultValue: "Self.defaultName")
open var name: String
{
@storageRestrictions(initializes: _name)
init(initialValue) {
#warning("[Macro] name")
_name = initialValue
}
get {
observationRegistrar.access(self, keyPath: \.name)
return _name
}
set {
let oldValue = _name
_ = oldValue // Ignore warning
var newValue = newValue
if newValue.isEmpty != false {
newValue = Self.defaultName
}
newValue = validateName(newValue)
guard oldValue != newValue else {
return
}
observationRegistrar.withMutation(of: self, keyPath: \.name) {
notifyChange()
databaseManager.undoManager?.registerUndo(withTarget: self) {
$0.name = oldValue
}
databaseManager.undoManager?.setActionName(Self.nameUndoActionName)
_name = newValue
registerModification()
}
}
}
private func validateName(_ name: String) -> String {
return name.validPathIdentifier
}
open var defaultIcon: LAImage? {
return nil
}
@Attribute([.referenceComparison])
@Image(maxSize: CGSize(width: 32, height: 32), compareMaxSize: CGSize(width: 24, height: 24))
open var icon: LAImage?
{
@storageRestrictions(initializes: _icon)
init(initialValue) {
#warning("[Macro] icon")
_icon = initialValue
}
get {
observationRegistrar.access(self, keyPath: \.icon)
return _icon
}
set {
let oldValue = _icon
_ = oldValue // Ignore warning
guard oldValue !== newValue else {
return
}
observationRegistrar.withMutation(of: self, keyPath: \.icon) {
notifyChange()
databaseManager.undoManager?.registerUndo(withTarget: self) {
$0.icon = oldValue
}
databaseManager.undoManager?.setActionName(Self.iconUndoActionName)
_icon = newValue?.resized(toFit: CGSize(width: 32, height: 32), scale: 3)?.withRenderingMode(.alwaysOriginal)
registerModification()
}
}
}
open var isHidden = false
{
@storageRestrictions(initializes: _isHidden)
init(initialValue) {
#warning("[Macro] isHidden")
_isHidden = initialValue
}
get {
observationRegistrar.access(self, keyPath: \.isHidden)
return _isHidden
}
set {
let oldValue = _isHidden
_ = oldValue // Ignore warning
guard oldValue != newValue else {
return
}
observationRegistrar.withMutation(of: self, keyPath: \.isHidden) {
notifyChange()
databaseManager.undoManager?.registerUndo(withTarget: self) {
$0.isHidden = oldValue
}
databaseManager.undoManager?.setActionName(Self.isHiddenUndoActionName)
_isHidden = newValue
registerModification()
}
}
}
// MARK: Init
override public init(databaseManager: DatabaseManager, uniqueID: String = UUID().uuidString) {
name = Self.defaultName
super.init(databaseManager: databaseManager, uniqueID: uniqueID)
}
public enum CodingKeys: String, CodingKey, CaseIterable {
case name, icon, iconScale, isHidden
}
required public init(from decoder: any Decoder) throws {
let databaseManager = try decoder.databaseManager
_ = databaseManager // Ignore warning
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
if _name.isEmpty != false {
_name = Self.defaultName
}
icon = try container.decodeIfPresent(LAImage.self, forKey: .icon)
isHidden = try container.decode(Bool.self, forKey: .isHidden)
try super.init(from: decoder)
}
override open func encode(to encoder: any Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encodeIfPresent(icon, forKey: .icon)
try container.encode(isHidden, forKey: .isHidden)
}
override open class var columns: [String] {
return super.columns + [CodingKeys.name, CodingKeys.icon, CodingKeys.iconScale, CodingKeys.isHidden].map(\.rawValue)
}
required public init(databaseManager: LAData.DatabaseManager, row: DatabaseManager.AnyResultSet) throws {
let row = row.keyedBy(CodingKeys.self)
name = try row.decode(String.self, forKey: .name)
if _name.isEmpty != false {
_name = Self.defaultName
}
icon = try row.decodeIfPresent(LAImage.self, forKey: .icon)
isHidden = try row.decode(Bool.self, forKey: .isHidden)
try super.init(databaseManager: databaseManager, row: row)
}
override open func createPrepare() -> ((DatabaseManager.AnyQuery) throws -> Void) {
let prepare = super.createPrepare()
let name = name
let icon = icon
let isHidden = isHidden
return { query in
try prepare(query)
let query = query.keyedBy(CodingKeys.self)
try query.encode(name, forKey: .name)
try query.encodeIfPresent(icon, forKey: .icon)
try query.encode(isHidden, forKey: .isHidden)
}
}
required public init(databaseManager: LAData.DatabaseManager, object: LAData.DatabaseObject, mode: CopyMode) throws {
let object = try object.cast(Self.self)
name = object.name
if _name.isEmpty != false {
_name = Self.defaultName
}
icon = object.icon
isHidden = object.isHidden
try super.init(databaseManager: databaseManager, object: object, mode: mode)
}
override open func compare(with otherObject: LAData.DatabaseObject) throws -> [SynchronizationChange] {
var changes = try super.compare(with: otherObject)
let otherObject = try otherObject.cast(Self.self)
changes.appendIfNotNull(SynchronizationChange.compare(value: name, of: self, with: otherObject.name, of: otherObject, key: CodingKeys.name.rawValue, table: .libraryObject, action: { object, name in
object.name = name
}))
changes.appendIfNotNull(SynchronizationChange.compare(value: icon, of: self, with: otherObject.icon, of: otherObject, key: CodingKeys.icon.rawValue, table: .libraryObject, action: { object, icon in
object.icon = icon
}, maxSize: CGSize(width: 24, height: 24)))
changes.appendIfNotNull(SynchronizationChange.compare(value: isHidden, of: self, with: otherObject.isHidden, of: otherObject, key: CodingKeys.isHidden.rawValue, table: .libraryObject, action: { object, isHidden in
object.isHidden = isHidden
}))
return changes
}
private static var nameUndoActionName: String {
return String(localized: "name", table: .libraryObject)
}
private static var iconUndoActionName: String {
return String(localized: "icon", table: .libraryObject)
}
private static var isHiddenUndoActionName: String {
return String(localized: "isHidden", table: .libraryObject)
}
override open func assertIsIdentical(to otherObject: LAData.DatabaseObject) throws {
try super.assertIsIdentical(to: otherObject)
guard let otherObject = try? otherObject.cast(Self.self) else {
throw DatabaseError.unsatisfiedConstraint("type is different")
}
_ = otherObject // Ignore warning
guard name == otherObject.name else {
throw DatabaseError.unsatisfiedConstraint("name is different")
}
guard isHidden == otherObject.isHidden else {
throw DatabaseError.unsatisfiedConstraint("isHidden is different")
}
}
}
I have about 30 model classes like that in my project and most are more complicated than this one. I think my build time after a clean is like 10x it was before.