How to set addObserver in SwiftUI?

14,997

Solution 1

This worked for me

   let NC = NotificationCenter.default



   self.NC.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, 
                       using: self.VPNDidChangeStatus)


   func VPNDidChangeStatus(_ notification: Notification) {


    }

Solution 2

The accepted answer may work but is not really how you're supposed to do this. In SwiftUI you don't need to add an observer in that way.

You add a publisher and it still can listen to NSNotification events triggered from non-SwiftUI parts of the app and without needing combine.

Here as an example, a list will update when it appears and when it receives a notification, from a completed network request on another view / controller or something similar etc.

If you need to then trigger an @objc func for some reason, you will need to create a Coordinator with UIViewControllerRepresentable

struct YourSwiftUIView: View {

    let pub = NotificationCenter.default
            .publisher(for: NSNotification.Name("YourNameHere"))


    var body: some View {
        List() {
            ForEach(userData.viewModels) { viewModel in
                SomeRow(viewModel: viewModel)
            }
        }
        .onAppear(perform: loadData)
        .onReceive(pub) { (output) in
            self.loadData()
        }
    }

    func loadData() {
        // do stuff
    }
}

Solution 3

I have one approach for NotificationCenter usage in SwiftUI.

For more information Apple Documentation

Notification extension

extension NSNotification {
    static let ImageClick = Notification.Name.init("ImageClick")
}

ContentView

struct ContentView: View {
    var body: some View {
        VStack {
            DetailView()
        }
        .onReceive(NotificationCenter.default.publisher(for: NSNotification.ImageClick))
        { obj in
           // Change key as per your "userInfo"
            if let userInfo = obj.userInfo, let info = userInfo["info"] {
              print(info)
           }
        }
    }
}

DetailView

struct DetailView: View {
    var body: some View {
        Image(systemName: "wifi")
            .frame(width: 30,height: 30, alignment: .center)
            .foregroundColor(.black)
            .onTapGesture {
                NotificationCenter.default.post(name: NSNotification.ImageClick, 
                                                object: nil, userInfo: ["info": "Test"])
        }
    }
}

Solution 4

I use this extension so it's a bit nicer on the call site:

/// Extension

extension View {
    func onReceive(_ name: Notification.Name,
                   center: NotificationCenter = .default,
                   object: AnyObject? = nil,
                   perform action: @escaping (Notification) -> Void) -> some View {
        self.onReceive(
            center.publisher(for: name, object: object), perform: action
        )
    }
}

/// Usage

struct MyView: View {
    var body: some View {
        Color.orange
            .onReceive(.myNotification) { _ in
                print(#function)
            }
    }
}

extension Notification.Name {
    static let myNotification = Notification.Name("myNotification")
}

Solution 5

It is not SwiftUI-native approach, which is declarative & reactive. Instead you should use NSNotificationCenter.publisher(for:object:) from Combine.

See more details in Apple Documentation

Share:
14,997
O-mkar
Author by

O-mkar

Updated on June 03, 2022

Comments

  • O-mkar
    O-mkar about 2 years

    How do I add NotificationCenter.default.addObserve in SwiftUI?

    When I tried adding observer I get below error

    Argument of '#selector' refers to instance method 'VPNDidChangeStatus' that is not exposed to Objective-C

    But when I add @objc in front of func I get below error

    @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes

    Here is my code

    let NC = NotificationCenter.default
    
    var body: some View {
         VStack() {
    
         }.onAppear {
    
               self.NC.addObserver(self, selector: #selector(self.VPNDidChangeStatus),
                                  name: .NEVPNStatusDidChange, object: nil)
    
         }
    } 
    
    @objc func VPNDidChangeStatus(_ notification: Notification) {
        //    print("VPNDidChangeStatus", VPNManager.shared.status)
    }