With #13's addition of @Init(assignee:type:), MemberwiseInit's @Init became expressive enough to support property-wrapped members[^1].
But, MemberwiseInit should provide a cleaner way to initialize properties wrapped by property wrappers. Further, @Init has become burdened by some seldomly needed parameters and is now Xcode autocomplete-unfriendly. We can improve the situation somewhat.
Here are the new macro definitions:
@Init for standard property initialization behavior.
@InitWrapper to initialize the wrapper of a property-wrapped property.
@InitRaw directly exposes the full configurability of the macro.
// An escape hatch that embraces the "template" nature of the macro and directly exposes it's configuration.
// 6 arguments
public macro InitRaw(
_ accessLevel: AccessLevelConfig? = nil,
assignee: String? = nil,
default: Any? = nil, // forward looking
escaping: Bool? = nil,
label: String? = nil,
type: Any.Type? = nil
) = …
// To simplify common usage, forgo 'assignee' and 'type'.
// 4 arguments
public macro Init(
_ accessLevel: AccessLevelConfig? = nil,
default: Any? = nil, // forward looking
escaping: Bool? = nil,
label: String? = nil
) = …
// Use the 'assignee' of 'self._\(#propertyName)'.
// 5 arguments
public macro InitWrapper(
_ accessLevel: AccessLevelConfig? = nil,
default: Any? = nil, // forward looking
escaping: Bool? = nil,,
label: String? = nil,
type: Any.Type // NB: 'type' is required because it can't be inferred, and will always be different than that of the wrapped property
) = …
Example:
import SwiftUI
@propertyWrapper
struct Logged<Value> {
var wrappedValue: Value {
didSet {
print("Logged: \(wrappedValue)")
}
}
init(wrappedValue: Value) {
self.wrappedValue = wrappedValue
}
}
// NB: Some property wrappers require initialization of the property
// wrapper itself, hence `@InitWrapper`. Here, we want Logged to be
// initialized without triggering its side effects (logging).
@MemberwiseInit(.public)
public struct CounterView: View {
// @Logged: InitWrapper
@InitWrapper(.public, default: "Blob", type: Logged<String>)
@Logged
var name1: String
// @Binding: InitWrapper
@InitWrapper(.public, type: Binding<Int>)
@Binding
var count1: Int
// @Logged: InitRaw
@InitRaw(.public, assignee: "self._name1", default: "Blob", type: Logged<String>)
@Logged
var name2: String
// @Binding: InitRaw
@InitRaw(.public, assignee: "self._count1", type: Binding<Int>)
@Binding
var count2: Int
// @State
@Init(.public)
@State var isOn = false
var body: some View { … }
}
[^1]: I think@Init(assignee:type:) provides complete (if awkward) support for initializing property wrappers. If I'm wrong, I'd love to hear about it.
With #13's addition of
@Init(assignee:type:)
, MemberwiseInit's@Init
became expressive enough to support property-wrapped members[^1].But, MemberwiseInit should provide a cleaner way to initialize properties wrapped by property wrappers. Further,
@Init
has become burdened by some seldomly needed parameters and is now Xcode autocomplete-unfriendly. We can improve the situation somewhat.Here are the new macro definitions:
@Init
for standard property initialization behavior.@InitWrapper
to initialize the wrapper of a property-wrapped property.@InitRaw
directly exposes the full configurability of the macro.Example:
[^1]: I think
@Init(assignee:type:)
provides complete (if awkward) support for initializing property wrappers. If I'm wrong, I'd love to hear about it.swift-memberwise-init-macro version information
0.2.0