Aleph
Aleph

Reputation: 593

Detecting application becoming going to the background and coming back to the foreground

I have a macOS app that needs to do some cleanups when the app looses focus (goes to the background), and some reloading of stuff when the it gains focus again (the app is now back in the foreground).

I have tried this code, both on the views and on the main app scene:

struct ContentView: View {
    @Environment(\.scenePhase) var scenePhase

    var body: some View {
        Text("Hello, world!")
            .padding()
            .onChange(of: scenePhase) { newPhase in
                if newPhase == .active {
                    print("Active")
                } else if newPhase == .inactive {
                    print("Inactive")
                } else if newPhase == .background {
                    print("Background")
                }
            }
    }
}

But I only receive an active state. Nothing else. I have also tried:

.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { (_) in
          print("UIApplication: active")
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { (_) in
          print("UIApplication: background")
}

But I can't get it to work.

How can I detect when the application goes to the background, and then the user brings it back to the front to work on it?

EDIT:

I created an appdelegate and it triggers, but only sometimes

class AppDelegate: NSObject, NSApplicationDelegate {

    func applicationDidBecomeActive(_ aNotification: Notification) {
        print(">> app coming back, reloading data...")
    }

}

Upvotes: 2

Views: 1338

Answers (3)

ingconti
ingconti

Reputation: 11646

I got here searching something similar. After googling a bit I found some tips from:

https://www.swiftbysundell.com/tips/observing-combine-publishers-in-swiftui-views/

I tested it for macOS and iOS in and work in a cross-portable fashion:

import SwiftUI

#if os(iOS)
import UIKit
#elseif os(macOS)
import AppKit
#endif


struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
        }
        .onAppEnteredBackground {
            print("going back")
        }
    }
}

//MARK: extension


#if os(iOS)
fileprivate let GO_DOWN = UIApplication.didEnterBackgroundNotification
#elseif os(macOS)
fileprivate let GO_DOWN = NSApplication.didResignActiveNotification
#endif



extension View {
    
    func onNotification(
        _ notificationName: Notification.Name,
        perform action: @escaping () -> Void
    ) -> some View {
        onReceive(NotificationCenter.default.publisher(
            for: notificationName
        )) { _ in
            action()
        }
    }
    
    func onAppEnteredBackground(
        perform action: @escaping () -> Void
    ) -> some View {
        onNotification(
            GO_DOWN,
            perform: action
        )
    }
}

Upvotes: 0

Aleph
Aleph

Reputation: 593

The original code I wrote worked:

.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification))

Added this as to the view:

struct ContentView: View {

        var body: some View {
            Text("Hello, world!")
                .padding()
                .onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) {
                     print("coming back!")
                 }
    }

But I had the project being Multiplatform and it was creating some issues. I turned the project into a macOS only project and it worked.

Upvotes: 1

ChrisR
ChrisR

Reputation: 12125

this always worked for me:

@main
struct MyApp: App {

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        
        .onChange(of: scenePhase) { phase in
            if phase == .inactive || phase == .background {
                // save
            }
        }
    }
}

Upvotes: 1

Related Questions