Clone Struct Models for SwiftUI with No Anxiety
RayedDev
Posted on February 19, 2024
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.
Posted on February 19, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.