Skip to content

Commit 73cdfce

Browse files
gfreezyclaude
andcommitted
Exclude ObservableOnly properties from notification handler
In observeFirst mode, filter out @ObservableOnly properties from the didChangeNotification handler since they are not stored in UserDefaults and cannot be triggered by UserDefaults changes. This simplifies the generated code and prevents dead case branches. Use compactMap during metas construction to exclude non-backed properties, removing the need for the isBacked discriminator in caseCode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d9b6335 commit 73cdfce

1 file changed

Lines changed: 11 additions & 23 deletions

File tree

Sources/ObservableDefaultsMacros/Macros/ObservableDefaultsMacro.swift

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,15 @@ extension ObservableDefaultsMacros: MemberMacro {
148148
return varDecl
149149
}
150150

151-
// Build mapping between properties and their UserDefaults keys
152-
let metas: [(userDefaultsKey: String, propertyID: String, isBacked: Bool)] = persistentProperties
153-
.map { property in
151+
// Build mapping between properties and their UserDefaults keys.
152+
// In observeFirst mode, only explicitly @DefaultsBacked properties are backed;
153+
// non-backed (ObservableOnly) properties are excluded because they are not
154+
// stored in UserDefaults and should not appear in the notification handler.
155+
let metas: [(userDefaultsKey: String, propertyID: String)] = persistentProperties
156+
.compactMap { property in
157+
if observeFirst, !property.hasAttribute(named: DefaultsBackedMacro.name) {
158+
return nil
159+
}
154160
let key =
155161
property.attributes.extractValue(
156162
forAttribute: DefaultsBackedMacro.name,
@@ -159,10 +165,7 @@ extension ObservableDefaultsMacros: MemberMacro {
159165
forAttribute: DefaultsKeyMacro.name,
160166
argument: DefaultsKeyMacro.key) ?? property.identifier?.text ?? ""
161167
let propertyID = property.identifier?.text ?? ""
162-
// In observeFirst mode, only explicitly @DefaultsBacked properties are backed;
163-
// otherwise all persistent properties are backed.
164-
let isBacked = !observeFirst || property.hasAttribute(named: DefaultsBackedMacro.name)
165-
return (key, propertyID, isBacked)
168+
return (key, propertyID)
166169
}
167170

168171
// Generate keyPath mapping for external change handling
@@ -181,22 +184,7 @@ extension ObservableDefaultsMacros: MemberMacro {
181184
let caseCode = metas.enumerated().map { index, meta in
182185
let caseIndent = index == 0 ? "" : " "
183186
// swiftformat:disable all
184-
if !meta.isBacked {
185-
// ObservableOnly properties are not stored in UserDefaults,
186-
// so we cannot compare values — keep the original behavior.
187-
if hasMainActor {
188-
return """
189-
\(caseIndent)case prefix + "\(meta.userDefaultsKey)":
190-
\(caseIndent) MainActor.assumeIsolated {
191-
\(caseIndent) host._$observationRegistrar.withMutation(of: host, keyPath: \\.\(meta.propertyID)) {}
192-
\(caseIndent) }
193-
"""
194-
} else {
195-
return """
196-
\(caseIndent)case prefix + "\(meta.userDefaultsKey)": host._$observationRegistrar.withMutation(of: host, keyPath: \\.\(meta.propertyID)) {}
197-
"""
198-
}
199-
} else if hasMainActor {
187+
if hasMainActor {
200188
return """
201189
\(caseIndent)case prefix + "\(meta.userDefaultsKey)":
202190
\(caseIndent) MainActor.assumeIsolated {

0 commit comments

Comments
 (0)