How to fix onChange(of:perform:) deprecation warnings in iOS 17
Fix the onChange(of:perform:) deprecation warnings in iOS 17.
If you're in the process of updating your SwiftUI apps for iOS 17, you are probably running into this warning:
'onChange(of:perform:)' was deprecated in iOS 17.0: Use `onChange` with a two or zero parameter action closure instead.
I found this warning to be confusing. "Use 2 parameters...or zero parameters?" "What's wrong with using the one parameter I'm already using?" It was only in writing this post that I figured out the why behind Apple's reasoning for this change. By forcing myself to try and write about this warning, I figured it out.
What's Changed?
Essentially, Apple deprecated the existing instance method in favor of two new methods:
And:
You'll notice those methods have some subtle, but important differences in their method signature. The first uses these:
value
The value to check against when determining whether to run the closure.
initial
Whether the action should be run when this view initially appears.
action
A closure to run when the value changes.
Notice that the action
closure takes no parameters. Using this, we no longer have to pass anything into our closure to get the value. The observed value comes along for the ride automatically. Thus we have zero arguments coming into our action
closure.
That second link, points to a similar version, with the key difference being the two values coming into our action
closure: oldValue
and newValue
.
value
The value to check against when determining whether to run the closure.
initial
Whether the action should be run when this view initially appears.
action
A closure to run when the value changes.
oldValue
The old value that failed the comparison check (or the initial value when requested).
newValue
The new value that failed the comparison check.
In the second instance, we have two new arguments we can pass into the closure oldValue
and newValue
, yielding two arguments.
The first is cleaner, easier to reason about, and simply less code. The second is more powerful.
You might have also notices that both methods have a new initial
parameter, which will allow you to decide if the closure should run when the view initially appears. I haven't yet needed to do that, but I expect this is something some developers might be very excited about. The SwiftUI team has taken the old .onChange()
modifier and made it cleaner and more powerful with these new methods. Sweet.
How to fix this warning
Your code might've looked something like this up until now. With one value argument coming into your trailing closure.
If that "trailing closure" terminology sounds funny to you or is something you still haven't quite wrapped your head around yet, forget about it for now. It took me a real long time to get it too.
Anyways, your code might look something like this today:
.onChange(of: selectedIcon) { tag in
if tag == "Default" {
changeAppIcon(to: nil)
} else {
changeAppIcon(to: tag)
}
}
With this version of .onChange()
being deprecated, Apple is essentially saying the following with their warning:
"Hey, stop using this. We're going to remove it some time in the future and this will totally stop working in your app. Use these other versions of .onChange()
instead."
By dropping the tag in
bits and using the selectedIcon
argument instead, we can adopt the new, preferred way of using .onChange()
and silence the Xcode warning.
.onChange(of: selectedIcon) {
if selectedIcon == "Default" {
changeAppIcon(to: nil)
} else {
changeAppIcon(to: selectedIcon)
}
}
oldValue and newValue version
In some cases, you might want to use the old value and the new value of the property you are observing. In those cases, you can now do the following:
.onChange(of: someState) { oldState, newState in
model.yourDidChangeMethod(from: oldState, to: newState)
}
This is that "two parameter action closure" that Apple mentions in their warning. Adopting whichever suits your view will silence the warning.
🤙