Detect foreground/background change

In ios17 and above, I can detect when swiftUI app goes into and out of background using:

@Environment(\.scenePhase) private var phase

... .onChange(of: phase) { switch phase { case .background: print("entering background...") case .active: print("entering foreground...") default: break } }

Is there a straightforward way to do this in SwiftUI for ios 16? I've found some examples using UIKit, but I am not that swift (sorry, couldn't resist) with that framework.

  • I thought scenePhase was introduced in iOS 14?

  • it's the onChange(of: phase) that is giving me the error in xcode...says it's not supported before ios 17. Maybe there is a variation of this modifier.

Add a Comment

Replies

Are you familiar with Combine? If so, you can bridge from the UIKit ‘did enter background’ notification to a publisher like so:

let publisher = NotificationCenter.default
    .publisher(for: UIApplication.didEnterBackgroundNotification)
    .receive(on: DispatchQueue.main)

Similarly with the ‘did enter foreground’ one:

let publisher = NotificationCenter.default
    .publisher(for: UIApplication.willEnterForegroundNotification)
    .receive(on: DispatchQueue.main)

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • Thanks! I'll give it a try.

Add a Comment

The scenePhase modifier hasn't changed recently, but onChange has. There are three versions of that modifier, two of which are new in iOS 17:

And one of which is deprecated in iOS 17, but available back to iOS 14:

The main difference between these modifiers is how many arguments their trailing closure takes. The new modifiers take zero or two arguments, respectively, and you are implicitly using the first of these, with zero arguments passed to the closure:

.onChange(of: phase) {
    switch phase {
        case .background: print("entering background...")
        case .active: print("entering foreground...")
        default: break
    }
}

You can add an argument to the closure to tell Xcode to use the deprecated method instead:

.onChange(of: phase) { newPhase in
    switch newPhase {
        case .background: print("entering background...")
        case .active: print("entering foreground...")
        default: break
    }
}

That should work for earlier versions of iOS.

Perfect. I can detect the OS version and conditionally run the appropriate block, and then phase out the deprecated code eventually. Thanks!