I've been working with SwiftUI for some time, and the task of copying struct models has always been a source of anxiety for me. To illustrate, let's consider this example:
struct Person {
let name: String
var active: Bool
var score: Int
}
var persons = [
Person(name: "Alice", active: false, score: 0),
Person(name: "Bob", active: false, score: 0),
Person(name: "Charlie", active: false, score: 0)
]
// Using map to change the active status to true and increase the score value by one for all persons
persons = persons.map { person in
var modifiedPerson = person
modifiedPerson.active = true
modifiedPerson.score += 1
return modifiedPerson
}
Now, what if I have conditional logic for scoring and activating? Then, I would make this change:
persons = persons.map { person in
if allowedNames.contains(person.name) {
var modifiedPerson = person
modifiedPerson.active = true
modifiedPerson.score += 1
return modifiedPerson
}
return person
}
However, I believe there's a better way that aligns more closely with the immutability pattern that SwiftUI aims to achieve. This can be done using the swift-immutable
Swift macro package, which allows cloning a model and partially modifying its members.
Let's see how it works.
First, we add the package.
Then, we add the Clone macro to our struct.
import SwiftImmutable
@Clone
struct Person {
let name: String
var active: Bool
var score: Int
}
The macro will add a clone function to the struct, allowing us to use it like this:
// Using map to change the active status to true and increase the score value by one for all persons
persons = persons.map {
$0.clone(active: true, score: $0.score + 1)
}
Or using sugar syntax:
// Using map to change the active status to true and increase the score value by one for all persons
persons = persons.map {
$0.clone(toggle: .active, inc: .score(1))
}
What if there is a condition?
// Using map to change the active status to true and increase the score value by one for all persons
persons = persons.map {
!allowedNames.contains($0.name)
? $0
: $0.clone(toggle: .active, inc: .score(1))
}
Numeric, String, and Bool Modifiers
// Increment score
person = person.clone(inc: .score(1))
// Toggle active status
person = person.clone(toggle: .active)
// Add prefix to name
person = person.clone(prefix: .name("Mr. "))
// Append suffix to name
person = person.clone(suffix: .name(" Legend"))
I hope you find this helpful for your work. Thank you.
Top comments (0)