Skip to content

Commit 56181c3

Browse files
author
David Scheutz
committed
add support for structs with custom init
1 parent da4ad52 commit 56181c3

File tree

2 files changed

+96
-58
lines changed

2 files changed

+96
-58
lines changed

Binaries/Sourcery.artifactbundle/sourcery-2.1.7/Templates/SwiftCopy.stencil

Lines changed: 92 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -30,67 +30,96 @@ public enum OptionalCopy<T> {
3030
}
3131
}
3232

33+
{% macro funcParameter type property isLast %}
34+
{% ifnot property.isOptional %}
35+
{{ property.name }}: {{ property.typeName }}? = nil{% ifnot isLast %}, {% endif %}
36+
{% else %}
37+
{{ property.name }}: {{ type.name }}OptionalCopy<{{ property.typeName.unwrappedTypeName }}> = .noChange{% ifnot isLast %}, {% endif %}
38+
{% endif %}
39+
{% endmacro %}
40+
41+
{% macro initParameter type property isLast %}
42+
{% ifnot property.isOptional %}
43+
{{ property.name }}: {{ property.name }} ?? self.{{ property.name }}{% ifnot isLast %}, {% endif %}
44+
{% else %}
45+
{{ property.name }}: {{ property.name }}.update(using: self.{{ property.name }}){% ifnot isLast %}, {% endif %}
46+
{% endif %}
47+
{% endmacro %}
48+
49+
{% macro iterate type generate %}
50+
{% if type.allMethods|initializer|count == 0 %}
51+
{% for property in type.variables|instance|!computed where property.readAccess != "private" and property.readAccess != "fileprivate" and property.defaultValue == nil %}
52+
{% call generate type property forloop.last %}
53+
{% endfor %}
54+
{% endif %}
55+
56+
{% for initializer in type.allMethods|initializer %}
57+
{% for parameter in initializer.parameters %}
58+
{% call generate type parameter forloop.last %}
59+
{% endfor %}
60+
{% endfor %}
61+
{% endmacro %}
62+
3363
import SwiftUI
3464

3565
{% for type in types.based.Copyable %}
3666
{% if type.variables.count > 0 %}
3767
{% call includeImports type %}
38-
68+
3969
// MARK: {{ type.name }} - Copy
4070

4171
extension {{ type.name }} {
4272
public typealias {{ type.name }}OptionalCopy<T> = OptionalCopy<T>
4373

4474
public func copy(
45-
{% for property in type.variables|instance|!computed where property.readAccess != "private" and property.readAccess != "fileprivate" and property.defaultValue == nil %}
46-
47-
{% ifnot property.isOptional %}
48-
{{ property.name }}: {{ property.typeName }}? = nil{% ifnot forloop.last %}, {% endif %}
49-
{% else %}
50-
{{ property.name }}: {{ type.name }}OptionalCopy<{{ property.typeName.unwrappedTypeName }}> = .noChange{% ifnot forloop.last %}, {% endif %}
51-
{% endif %}
52-
{% endfor %}
75+
{% call iterate type funcParameter %}
5376
) -> {{ type.name }} {
5477
return {{ type.name }}(
55-
{% for property in type.variables|instance|!computed where property.readAccess != "private" and property.readAccess != "fileprivate" and property.defaultValue == nil %}
56-
{% ifnot property.isOptional %}
57-
{{ property.name }}: {{ property.name }} ?? self.{{ property.name }}{% ifnot forloop.last %}, {% endif %}
58-
{% else %}
59-
{{ property.name }}: {{ property.name }}.update(using: self.{{ property.name }}){% ifnot forloop.last %}, {% endif %}
60-
{% endif %}
61-
{% endfor %}
78+
{% call iterate type initParameter %}
6279
)
6380
}
6481
}
6582

83+
{% macro privateValueCheck type property isLast %}
84+
{% if property.isOptional %}
85+
true {% ifnot forloop.last %} &&{% endif %} // optional '{{ property.name }}' not required to check
86+
{% else %}
87+
_{{ property.name }} != nil {% ifnot forloop.last %} &&{% endif %}
88+
{% endif %}
89+
{% endmacro %}
90+
91+
{% macro privateInitAssignment type property isLast %}
92+
{{ property.name }}: _{{ property.name }}{% ifnot forloop.last %}, {% endif %}
93+
{% endmacro %}
94+
6695
// MARK: {{ type.name }} - Builder
6796

68-
extension {{ type.name }} {
69-
final class Builder: ObservableObject {
70-
let objectDidChange = PassthroughSubject<Void, Never>()
71-
72-
{% for property in type.variables|instance|!computed where property.readAccess != "private" and property.readAccess != "fileprivate" and property.defaultValue == nil %}
97+
{% macro builderSetter type property isLast %}
7398
private var _{{ property.name }}: {{ property.typeName }}{% ifnot property.isOptional %}!{% endif %} {
7499
willSet { objectWillChange.send() }
75100
didSet { objectDidChange.send() }
76101
}
77-
102+
78103
var {{ property.name }}: {{ property.typeName }}{% ifnot property.isOptional %}?{% endif %} {
79104
get { _{{ property.name }} }
80105
set { _{{ property.name }} = newValue }
81106
}
82-
107+
83108
@discardableResult
84109
func with({{ property.name }}: {{ property.typeName }}) -> Self {
85110
_{{ property.name }} = {{ property.name }}
86111
return self
87112
}
88-
{% endfor %}
113+
{% endmacro %}
114+
115+
extension {{ type.name }} {
116+
final class Builder: ObservableObject {
117+
let objectDidChange = PassthroughSubject<Void, Never>()
118+
119+
{% call iterate type builderSetter %}
89120

90121
func readyToBuild() -> Bool {
91-
{% for property in type.variables|instance|!computed where property.readAccess != "private" and property.readAccess != "fileprivate" and property.defaultValue == nil and property.isOptional == false %}
92-
_{{ property.name }} != nil{% ifnot forloop.last %} &&{% endif %}
93-
{% endfor %}
122+
{% call iterate type privateValueCheck %}
94123
}
95124

96125
func buildSafely() throws -> {{ type.name }} {
@@ -103,9 +132,7 @@ extension {{ type.name }} {
103132

104133
func build() -> {{ type.name }} {
105134
{{ type.name }}(
106-
{%- for property in type.variables|instance|!computed where property.readAccess != "private" and property.readAccess != "fileprivate" and property.defaultValue == nil %}
107-
{{ property.name }}: _{{ property.name }}{% ifnot forloop.last %}, {% endif %}
108-
{% endfor -%}
135+
{% call iterate type privateInitAssignment %}
109136
)
110137
}
111138
}
@@ -115,31 +142,23 @@ extension {{ type.name }} {
115142

116143
import Combine
117144

118-
{% macro assignProperties type source %}
119-
{% for property in type.variables|instance|!computed where property.readAccess != "private" and property.readAccess != "fileprivate" %}
120-
_{{ property.name }} = {{ source }}.{{ property.name }}
121-
{% endfor %}
145+
{% macro privateAssignment type property isLast %}
146+
{{ property.name }}: _{{ property.name }}{% ifnot forloop.last %}, {% endif %}
122147
{% endmacro %}
123148

124-
extension {{ type.name }} {
125-
126-
protocol Changeable: ObservableObject {
127-
{% for property in type.variables|instance|!computed where property.readAccess != "private" and property.readAccess != "fileprivate" %}
149+
{% macro protocolMembers type property isLast %}
128150
var {{ property.name }}: {{ property.typeName }} { get set }
129-
{% endfor %}
130-
}
131-
132-
func updater() -> Updater {
133-
.init({{ type.name|lowerFirstLetter }}: self)
134-
}
151+
{% endmacro %}
135152

136-
final class Updater: Changeable {
137-
private let initial: {{ type.name }}
138-
private var notifyUpdates = true
139-
140-
let objectDidChange = PassthroughSubject<Void, Never>()
141-
142-
{% for property in type.variables|instance|!computed where property.readAccess != "private" and property.readAccess != "fileprivate" %}
153+
{% macro sourceAssignment type property isLast %}
154+
_{{ property.name }} = {{ type.name|lowerFirstLetter }}.{{ property.name }}
155+
{% endmacro %}
156+
157+
{% macro assignProperties type %}
158+
{% call iterate type sourceAssignment %}
159+
{% endmacro %}
160+
161+
{% macro updaterSetter type property isLast %}
143162
private var _{{ property.name }}: {{ property.typeName }} {
144163
willSet { if notifyUpdates { objectWillChange.send() } }
145164
didSet { if notifyUpdates { objectDidChange.send() } }
@@ -155,11 +174,29 @@ extension {{ type.name }} {
155174
_{{ property.name }} = {{ property.name }}
156175
return self
157176
}
158-
{% endfor %}
177+
{% endmacro %}
178+
179+
extension {{ type.name }} {
180+
181+
protocol Changeable: ObservableObject {
182+
{% call iterate type protocolMembers %}
183+
}
184+
185+
func updater() -> Updater {
186+
.init({{ type.name|lowerFirstLetter }}: self)
187+
}
188+
189+
final class Updater: Changeable {
190+
private let initial: {{ type.name }}
191+
private var notifyUpdates = true
192+
193+
let objectDidChange = PassthroughSubject<Void, Never>()
194+
195+
{% call iterate type updaterSetter %}
159196

160197
init({{ type.name|lowerFirstLetter }}: {{ type.name }}) {
161198
self.initial = {{ type.name|lowerFirstLetter }}
162-
{% call assignProperties type type.name|lowerFirstLetter %}
199+
{% call assignProperties type %}
163200
}
164201

165202
{% if type.based.Equatable %}
@@ -172,7 +209,7 @@ extension {{ type.name }} {
172209
notifyUpdates = false
173210
objectWillChange.send()
174211

175-
{% call assignProperties type type.name|lowerFirstLetter %}
212+
{% call assignProperties type %}
176213

177214
notifyUpdates = true
178215
objectDidChange.send()
@@ -184,9 +221,7 @@ extension {{ type.name }} {
184221

185222
func build() -> {{ type.name }} {
186223
{{ type.name }}(
187-
{%- for property in type.variables|instance|!computed where property.readAccess != "private" and property.readAccess != "fileprivate" %}
188-
{{ property.name }}: _{{ property.name }}{% ifnot forloop.last %}, {% endif %}
189-
{% endfor -%}
224+
{% call iterate type privateInitAssignment %}
190225
)
191226
}
192227
}

SwiftCopyDemo/SwiftCopyDemo/Models.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@ struct User2: Equatable, Copyable {
2323

2424
struct User3: Equatable, Copyable {
2525
let id: Int
26-
26+
/// This should not generate a copy function because this property can't be assigned via constructor
27+
let hash: String
28+
2729
init(id: Int) {
2830
self.id = id
31+
hash = "\(id.hashValue)"
2932
}
3033

3134
/// This should not generate a copy function because it's a private property

0 commit comments

Comments
 (0)