Post

iOS 16 Compatible onChange with Old Values

Closure capture technique to access previous values in onChange on iOS 16.

iOS 16 Compatible onChange with Old Values

The Problem

iOS 17’s onChange(of:old:new:) provides both previous and current values. iOS 16 only has onChange(of:) with the new value.

Solution: Closure Capture

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import SwiftUI

public extension View {
    @ViewBuilder
    func onChangeCompat<V>(
        for value: V,
        _ action: @escaping (_ previous: V, _ current: V) -> Void
    ) -> some View where V: Equatable {
        if #available(iOS 17, *) {
            onChange(of: value, action)
        } else {
            onChange(of: value) { [value] newValue in
                action(value, newValue)
            }
        }
    }
}

The [value] capture list preserves the old value at the moment onChange is attached.

Practical Extension: onTransition

1
2
3
4
5
6
7
8
9
10
11
12
13
public extension View {
    func onTransition<V: Equatable>(
        of value: V,
        from reference: V,
        perform action: @escaping () -> Void
    ) -> some View {
        onChangeCompat(for: value) { oldValue, newValue in
            if oldValue == reference && newValue != reference {
                action()
            }
        }
    }
}

Triggers action when value moves away from a specific state:

1
2
3
4
5
6
@State private var loadingState: LoadingState = .loading

Text("Data")
    .onTransition(of: loadingState, from: .loading) {
        hapticFeedback.success()
    }

What do you think about this solution? Let me know in the comments!

☕ Support My Work

If you found this post helpful and want to support more content like this, you can buy me a coffee!

Your support helps me continue creating useful articles and tips for fellow developers. Thank you! 🙏

This post is licensed under CC BY 4.0 by the author.