Clone Struct Models for SwiftUI with No Anxiety

rayeddev

RayedDev

Posted on February 19, 2024

Clone Struct Models for SwiftUI with No Anxiety

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
}
Enter fullscreen mode Exit fullscreen mode

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   
}

Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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) 
}
Enter fullscreen mode Exit fullscreen mode

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)) 
}
Enter fullscreen mode Exit fullscreen mode

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)) 
}
Enter fullscreen mode Exit fullscreen mode

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"))
Enter fullscreen mode Exit fullscreen mode

I hope you find this helpful for your work. Thank you.

💖 💪 🙅 🚩
rayeddev
RayedDev

Posted on February 19, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related