Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI .onAppear, only running once

In a SwiftUI app I have code like this:

var body: some View {
    VStack {
        Spacer()
        ........
    }
    .onAppear {
      .... I want to have some code here ....
      .... to run when the view appears ....
    }
}

My problem is that I would like to run some code inside the .onAppear block, so that it gets run when the app appears on screen, after launching or after being in the background for a while. But it seems like this code is only run once at app launch, and never after. Am I missing something? Or should I use a different strategy to get the result I want?

like image 269
Michel Avatar asked Aug 15 '20 07:08

Michel


2 Answers

If you're linking against iOS14 then you can take advantage of the new scenePhase concept:

@Environment(\.scenePhase) var scenePhase

Where ever you are the Environment if injecting this property that you can test against three conditions:

switch newPhase {
    case .inactive:
        print("inactive")
    case .active:
        print("active")
    case .background:
        print("background")
}

So, all together:

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

    var body: some View {
        Text("Hello, World!")
            .onChange(of: scenePhase) { newPhase in
                switch newPhase {
                    case .inactive:
                        print("inactive")
                    case .active:
                        print("active")
                    case .background:
                        print("background")
                }
            }
    }
}
like image 67
valvoline Avatar answered Dec 08 '22 04:12

valvoline


You would have to observe the event when the app is entering foreground and publish it using @Published to the ContentView. Here's how:

struct ContentView: View {

    @ObservedObject var observer = Observer()

    var body: some View {
        VStack {
            Spacer()
            //...
        }
        .onReceive(self.observer.$enteredForeground) { _ in
            print("App entered foreground!") // do stuff here
        }
    }
}

class Observer: ObservableObject {

    @Published var enteredForeground = true

    init() {
        if #available(iOS 13.0, *) {
            NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIScene.willEnterForegroundNotification, object: nil)
        } else {
            NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
        }
    }

    @objc func willEnterForeground() {
        enteredForeground.toggle()
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}
like image 32
Frankenstein Avatar answered Dec 08 '22 05:12

Frankenstein