If you've worked with SwiftUI you've most likely come across property wrappers, @State
, @ObservableObject
are both implementations of property wrappers which are heavily used within the framework.
The @
syntax is likely all to you familiar to you (more so if you've worked with objc); we use @IBAction
and @IBOutlet
to reference our UI in code, things like @discardableResult
to ignore return types, now there's one more @
implementation to keep track of: property wrappers, but what are they, what do they do and how are they useful? In this article I hope to answer these questions and have you writing your own!
Property wrappers allow us to apply generalised functionality when defining our properties in swift. In practice this means we can perform operations on our types as they're defined, removing the need to call extra functions in our code to perform common tasks.
Let's look at an example: We have an app that loads user generated images from the server, we want to display these as a 300x300 square and place them inside a UIImageView
.
Defining a Property Wrapper
Property wrappers are simply classes or structs defined using the @propertyWrapper
attribute. The only requirement for a property wrapper is that it implements the wrappedValue
variable.
To keep this property wrapper generic we want to be able to pass in the size for our cropped image, just in case our designer decides to change the size from 300 to 100. When defining a property wrapper, swift will pass the variable being initialised into the wrappedValue
argument of our initialiser, so if we create an initialiser init(wrappedValue: UIImage, sqSize: Int)
we only need to pass in the sqSize
argument when we instantiate a variable implementing this functionality. Hopefully this will be clearer as we go.
Add a new file called Crop.swift
with the following code.
// 1
@propertyWrapper
struct Crop {
// 2
private var value: UIImage = UIImage()
private let sqSize: Int
// 3
var wrappedValue: UIImage {
get { value }
set {value = newValue}
}
// 4
init(wrappedValue: UIImage, sqSize: Int) {
self.sqSize = sqSize
self.wrappedValue = wrappedValue
}
}
- We tell swift our struct is a property wrapper using the
@propertyWrapper
attribute. - Define our variables, in this case the image we want to process and the crop size.
- Remember our requirement to implement
wrappedValue
? DefinewrappedValue
here, it can be any type you need it to be. We'll make it a computed property so we can perform our image processing later. - Define out initialiser and set our variables.
This is enough to get our property wrapper up and running, we can now define an image like so
@Crop(sqSize: 300) var image = UIImage(named:"my-img")
but nothing will happen.
Notice the function signature when using our property wrapper doesn't includewrappedValue
even though we require it in ourinit
method. When our variable is definedwrappedValue
is automatically passed in, so the value to the right of the=
operator is ourwrappedValue
parameter.
Now let's do something with it! Replace the code in set{}
with the following
set {
let centerX = Int(newValue.size.width / 2) - (sqSize / 2)
let centerY = Int(newValue.size.height / 2) - (sqSize / 2)
let imageRect = CGRect(x: centerX,
y: centerY,
width: sqSize,
height: sqSize)
if let cgImage = newValue.cgImage {
let cgImageRef = cgImage.cropping(to: imageRect)!
value = UIImage(cgImage: cgImageRef)
}
}
That's all we need for the property wrapper; when the setter is called on our wrappedValue
we perform the crop by finding the center x and center y of the image, then we create a new rect using the new coordinates (plus our sqSize
parameter) and call the CGImage
function cropping(to: CGRect)
to perform our crop. Now we have this let's use it in practice. Download the below image (right click -> Save As) and add it to Assets.xcassets
in your project.

Create a reference to this in ViewController.swift
class ViewController: UIViewController {
@Crop(sqSize: 400)
var image = UIImage(named: "swift-compiled-logo")!
...
We're going to add a UIImageView
in code and add it to our view
object, you can also do this in an xib or Storyboard if you'd prefer. Add the following function to ViewController.swift
.
func loadImageView() {
let imageView = UIImageView(image: self.image)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .center
self.view.addSubview(imageView)
NSLayoutConstraint.activate([
imageView.centerYAnchor.
constraint(equalTo: view.centerYAnchor),
imageView.centerXAnchor.
constraint(equalTo: view.centerXAnchor)
])
self.view.layoutIfNeeded()
}
Now replace viewDidLoad()
with the following.
override func viewDidLoad() {
super.viewDidLoad()
loadImageView()
}
That's it! If you build and run the app you should see the following.

Pretty cool, with the addition of property wrappers we've processed an image as we define it, we can now use this generic functionality for any image we have in our app.
That's it for now, if you haven't already i'd recommend looking at the swift documentation on property wrappers to find out what else they're capable of, this was just a small taste but should be everything you need to get started, the code for this blog post can be found on github.
Thanks for reading! ✌️