swift - SwiftUI: View method OnChange(of: scenePhase) does not accept 2-argument action - Stack Overflow

admin2025-04-30  1

In SwiftUI, I want to use onChange(of:, action:) function for scenePhase, but the build fails.

struct MyView: View {
    @Environment(\.scenePhase) var scenePhase
    @State private var timerStatus: TimerStatus

    var body: some View {
        VStack {
            Text(...)
            Button(...)
        }
        .onChange(of: scenePhase) { oldState, newState in // Error on this line
            if newState == .active {
                // some code
            } else if oldState == .active {
                // some code
            }
        }
        .onChange(of: timerStatus) { oldState, newState in
            // some code
        }
    }
}

enum TimerStatus {
    case paused
    case runningActive
    case runningInactive
}

I get the following error:

"Contextual closure type '(ScenePhase) -> Void' expects 1 argument, but 2 were used in closure body"

Do you have any idea why I get this error, and how to fix it?

I'm developing the program for iOS 18.2 in Xcode.

When I used "Jump to Definition" function for the onChange() function, the older version of onChange was shown:

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension View {
    @inlinable nonisolated public func onChange<V>(of value: V, perform action: @escaping (_ newValue: V) -> Void) -> some View where V : Equatable
}

onChange(of: timerStatus) works without the error and "Jump to Definition" shows the newer definition:

@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
extension View {
    nonisolated public func onChange<V>(of value: V, initial: Bool = false, _ action: @escaping (_ oldValue: V, _ newValue: V) -> Void) -> some View where V : Equatable
}

In SwiftUI, I want to use onChange(of:, action:) function for scenePhase, but the build fails.

struct MyView: View {
    @Environment(\.scenePhase) var scenePhase
    @State private var timerStatus: TimerStatus

    var body: some View {
        VStack {
            Text(...)
            Button(...)
        }
        .onChange(of: scenePhase) { oldState, newState in // Error on this line
            if newState == .active {
                // some code
            } else if oldState == .active {
                // some code
            }
        }
        .onChange(of: timerStatus) { oldState, newState in
            // some code
        }
    }
}

enum TimerStatus {
    case paused
    case runningActive
    case runningInactive
}

I get the following error:

"Contextual closure type '(ScenePhase) -> Void' expects 1 argument, but 2 were used in closure body"

Do you have any idea why I get this error, and how to fix it?

I'm developing the program for iOS 18.2 in Xcode.

When I used "Jump to Definition" function for the onChange() function, the older version of onChange was shown:

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension View {
    @inlinable nonisolated public func onChange<V>(of value: V, perform action: @escaping (_ newValue: V) -> Void) -> some View where V : Equatable
}

onChange(of: timerStatus) works without the error and "Jump to Definition" shows the newer definition:

@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
extension View {
    nonisolated public func onChange<V>(of value: V, initial: Bool = false, _ action: @escaping (_ oldValue: V, _ newValue: V) -> Void) -> some View where V : Equatable
}
Share Improve this question asked Jan 6 at 7:14 Kbb BioKbb Bio 11 bronze badge 2
  • 1 Could not replicate your issue, all works well for me. On MacOS 15.3, Xcode 16.2, target iOS-18.2, tested on real iOS device and MacCatalyst. What Xcode are you using? Note you should use @State private var timerStatus: TimerStatus = .paused, that is give it an initial value. – workingdog support Ukraine Commented Jan 6 at 7:26
  • Note that the error might be misleading and that the real issue is somewhere else. What happens if you comment out the whole onChange, what happens if you comment out what’s inside the closure for onChange? Do you get a different error, does it disappear? – Joakim Danielson Commented Jan 6 at 7:46
Add a comment  | 

1 Answer 1

Reset to default 0

The scenePhase environment value in SwiftUI reflects only the current lifecycle state of the app. It does not provide the oldValue in the onChange(of:) method.

Store the previous value in a separate variable:

@Environment(\.scenePhase) var scenePhase
@State private var previousScenePhase: ScenePhase?

var body: some View {
    VStack {
        Text("...")
        Button("...")
    }
    .onChange(of: scenePhase) { newState in
        if let oldState = previousScenePhase {
            if newState == .active {
                // Handle activation
            } else if oldState == .active {
                // Handle deactivation
            }
        }
        previousScenePhase = newState
    }
}
转载请注明原文地址:http://anycun.com/QandA/1745980454a91446.html