1

In SwiftUI, I have a network request running in scenedelegate, scenedidbecomeactive. I don't know which view the user will be on when the app becomes active, but I want to present an alert if the data in the network request changes. I simplified the code below, so it's easy to read...

 func sceneDidBecomeActive(_ scene: UIScene) {
            let customClass = CustomClass()
            customClass.performNetworkRequest()

In CustomClass, i have...

func performNetWorkRequest() {
URLSession.shared.dataTask(with: url) { (data, response, error) in          
      if let d = data {
         let response = try JSONDecoder().decode(DetailResponse.self, from: d)
             DispatchQueue.main.async {
                 //Here is where I want to either present an alert, but I can't figure out how to.
                 //OR do i put a func in SceneDeletegate to present the alert on the window.rootviewcontroller and then just call that func from here?
}

Any help is much appreciated!

c0nman
  • 189
  • 1
  • 12
  • 3
    Ideally, your network request class would present anything. It would just update some environment observed object and the relevant views would react. – Paulw11 Jun 09 '20 at 04:23
  • but i'm not sure which view the user is on because it's scenedidbecomeactive, so would i have to put an observed object into every view? that doesn't seem very scalable. I guess I could do it now because there are only 4-5 views. Ok, let me try it out. – c0nman Jun 09 '20 at 13:28
  • I would probably put it in the root view with a zstack that shows a "toast" view and your normal view. When there is an update you can show the toast for a few seconds on top of whatever else is showing. It doesn't sound like a situation where you would want to interrupt what the user is doing to actually show an alert, but you could do that. – Paulw11 Jun 09 '20 at 21:02

1 Answers1

1

Paul has a point - here's a possible implementation:

// In CustomClass.swift

import Combine

class CustomClass : ObservableObject {

    @Published var dataRecieved = PassthroughSubject<DetailResponse, Never>()

    init() {
        performNetWorkRequest()
    }

    func performNetWorkRequest() {
        URLSession.shared.dataTask(with: url) { (data, response, error) in

            let response = try JSONDecoder().decode(DetailResponse.self, from: data)

            DispatchQueue.main.async {
                self.dataRecieved.send(response)
            }
        }
        .resume()
    }
}

// In SomeView.swift

import SwiftUI
import Combine

struct ContentView: View {

    @State var showAlert = false
    var customClass = CustomClass()

    var body: some View {
        Text("Hello, World!")
            .onReceive(customClass.dataRecieved) { _ in
                self.showAlert = true
            }
            .alert(isPresented: $showAlert) {
            // your alert
            }
    }
}

Notice I didn't mention the SceneDelegate in any of these - this approach (called MVVM) is more flexible, in my opinion - besides, the way it is set up, performNetWorkRequest() will be executed as soon as your view is initialized, anyway. You can also tweak the PassthroughSubject - I didn't know if you needed the DetailResponse or not. Hope this helped!

Edit:

I just reread your question and it seems that this implementation is at fault as you noted there was no way to know what view the user would be on in the case of a network change. In that case, you can feed the same instance of CustomClass in your SceneDelegate as an EnvironmentObject.

akim
  • 93
  • 5
  • Thanks. Yeh, i guess i'll have to do this and then add an observed object to each view that the user could be on. – c0nman Jun 09 '20 at 13:36
  • Worked like a charm! – c0nman Jun 11 '20 at 02:46
  • If all the views are descendants of a single view (say a NavigationView or TabView) then you can just have that one view present the alert, and it will be seen no matter what descendent view is topmost. But what about if a fullScreenCover or Sheet is being presented and you want the alert to appear over it? In that case the attempt by the root view will fail. It would be great to have a way of presenting an alert over whatever is on the screen. Any ideas, @akim? – Anton Mar 19 '21 at 17:45