How to change Struct property while setting?

How can I make setter to perform rounding of the width value during setting up the size.width using extension or inside Shape struct/setter without knowing letting caller to do it?


Struct Shape {
  var size: CGSize = .zero
}

let shape1  = Shape()
shape1.size.width = 1.95

print(shape1.size.width) // 1.95

// Do not round here but in the Shape struct or extension 
print(round(shape1.size.width)) // 2.0 is the expected answer 

If you set the value again in didSet, it will set the value without recursively triggering another didSet call.

3 Likes

Thank you.

A few ways:

/// rounds on set
struct Shape1 {
    var _size: CGSize = .zero
    
    var size: CGSize {
        get { _size }
        set {
            _size = CGSize(width: round(newValue.width), height: round(newValue.height))
        }
    }
}

/// rounds on getting roundSize
struct Shape2 {
    var size: CGSize
    var roundSize: CGSize {
        CGSize(width: round(size.width), height: round(size.height))
    }
}

/// rounds on set
struct Shape3 {
    var size: CGSize {
        didSet {
            let roundSize = CGSize(width: round(size.width), height: round(size.height))
            if roundSize != size {
                size = roundSize
            }
        }
    }
}
1 Like

You can simplify this with a didSet observer, and remove the need for a separate "backing" stored property

/// rounds on set
struct Shape1 {
    var size: CGSize {
        didSet {
            _size = CGSize(width: round(size.width), height: round(size.height))
        }
    }
}
1 Like

This might not be worth doing in this case, but I'll note that you can extract property observation/getter/setter behaviour into a property wrapper, so that it can have a name and be easily reused. Here's an example that does the rounding on every set

import CoreGraphics

@propertyWrapper
struct RoundedSize {
    var wrappedValue: CGSize {
        didSet {
            wrappedValue = CGSize(width: round(wrappedValue.width), height: round(wrappedValue.height))
        }
    }
}

struct Shape {
    @RoundedSize var size: CGSize = .zero
}

var shape1 = Shape()
shape1.size.width = 1.95

print(shape1.size.width) // 2.0
1 Like

You mean that didSet is not called when setting a variable from inside "didSet" itself? Good to know.

I must be blind. This is exactly what @harlanhaskins wrote, so deleting my message that stated the same ;-)