How to set addObserver in SwiftUI?
This worked for me
let NC = NotificationCenter.default
self.NC.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil,
using: self.VPNDidChangeStatus)
func VPNDidChangeStatus(_ notification: Notification) {
}
Refresh SwiftUI View Based on Local Notification
The transitive nature of SwiftUI views makes doing things like trying to capture a reference to self
in your view's init
problematic. Here are a couple solutions:
Option 1
Keep everything in the View
:
struct ContentView: View {
@State private var isDataImported: Bool = false
@State private var cancellable : AnyCancellable?
var body: some View {
Group {
if isDataImported {
Text("Has data")
} else {
Text("Does not have data")
}
}.onAppear {
cancellable = NotificationCenter.default.publisher(for: .onDataImported)
.receive(on: RunLoop.main)
.sink { notification in
self.isDataImported = true
}
NotificationCenter.default.post(name: .onDataImported, object: nil)
}
}
}
Option 2
This is usually what I'd do, moving it to a view model, so that you can keep onAppear
a little cleaner. Because the view model is a class
and have a reliable, reference-based lifetime, the assignment to self
is less problematic:
class ViewModel: ObservableObject {
@Published var isDataImported: Bool = false
private var cancellable : AnyCancellable?
init() {
cancellable = NotificationCenter.default.publisher(for: .onDataImported)
.receive(on: RunLoop.main)
.sink { notification in
self.isDataImported = true
}
}
}
struct ContentView : View {
@StateObject var viewModel = ViewModel()
var body: some View {
Group {
if viewModel.isDataImported {
Text("Has data")
} else {
Text("Does not have data")
}
}.onAppear {
NotificationCenter.default.post(name: .onDataImported, object: nil)
}
}
}
Why I can't receive a notification on an observer class inside a View struct in SwiftUI?
Notifications matched by object, so if you subscribe by one object but post with another object, then subscriber is not fired.
Here is fixed variant. Tested with Xcode 12.1.
NotificationCenter.default.addObserver(observerA,
selector: #selector(ObserverClassA.receivedNotification(notification:)),
name: Notification.Name("CustomNotification"),
object: nil) // << subscribe for all
let notificationToPost = Notification(name: Notification.Name("CustomNotification"), object: "This is the message", userInfo: nil)
NotificationCenter.default.post(notificationToPost)
SwiftUI with NotificationCenter publishers
@State
is not ready yet in init, so it cannot be used for such purposes. The approach can be as follows:
var cancellables = Set<AnyCancellable>()
init() {
NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)
.sink(receiveValue: { _ in
print(">> in init")
})
.store(in: &cancellables)
}
in such defined cancellables
you can store all subscribers created in init
, but you will not be able to use it later in code, but this approach is good for once defined notification handlers.
How to subscribe to EventKit changes in SwiftUI?
You have a couple of options. One is to use an ObservableObject
that can have @objc
functions and declare it as the delegate for the notifications:
struct ContentView: View {
@StateObject private var eventDelegate = EventDelegate()
var body: some View {
Text("Hello, world!")
.padding()
}
}
class EventDelegate: ObservableObject {
let eventStore = EKEventStore()
init() {
NotificationCenter.default.addObserver(self, selector: #selector(observeEvents), name: .EKEventStoreChanged, object: eventStore)
//remember to clean up your observers later
}
@objc func observeEvents() {
print("called with updated events")
}
}
A second option would be to use NotificationCenter
's publisher
inside your View
. Note this example is incomplete, as it's unclear what actions you want to take with the notification, but it's a template to get you started:
struct ContentView: View {
@State private var eventStore = EKEventStore() //normally wouldn't use @State for a reference type like this, but it seems pointless to recreate it
@State private var pub : NotificationCenter.Publisher?
var body: some View {
Text("Hello, world!")
.padding()
.onAppear {
pub = NotificationCenter.default.publisher(for: .EKEventStoreChanged, object: eventStore)
//use sink or assign here
}
}
}
NSNotificationCenter addObserver in Swift
It's the same as the Objective-C API, but uses Swift's syntax.
Swift 4.2 & Swift 5:
NotificationCenter.default.addObserver(
self,
selector: #selector(self.batteryLevelChanged),
name: UIDevice.batteryLevelDidChangeNotification,
object: nil)
If your observer does not inherit from an Objective-C object, you must prefix your method with @objc
in order to use it as a selector.
@objc private func batteryLevelChanged(notification: NSNotification){
//do stuff using the userInfo property of the notification object
}
See NSNotificationCenter Class Reference, Interacting with Objective-C APIs
How to set an observer on the queue player's currentItem?
Representable, which is struct, cannot be used KVO observer. You can to use Coordinator
as observer.
Here is modified code with possible approach:
struct VideoPlayerS : UIViewControllerRepresentable {
var work : WorkoutDeS
@Binding var player : AVQueuePlayer
var playerLayer = AVPlayerLayer()
public func makeUIViewController(context: Context) -> AVPlayerViewController {
let items = [
AVPlayerItem(url: URL(fileURLWithPath: String(work.url1))),
AVPlayerItem(url: URL(fileURLWithPath: String(work.url2)))
]
let player = AVQueuePlayer(items: items)
let controller = AVPlayerViewController()
DispatchQueue.main.async {
self.player = player
}
controller.player = player
controller.videoGravity = .resizeAspectFill
player.actionAtItemEnd = .none
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: .main) { _ in
self.player.seek(to: CMTime.zero)
self.player.play()
}
player.currentItem?.addObserver(context.coordinator, forKeyPath: "status", options: [.new], context: nil)
player.play()
return controller
}
func makeCoordinator() -> Coordinator {
Coordinator(owner: self)
}
class Coordinator: NSObject {
var owner : VideoPlayerS
init(owner: VideoPlayerS) {
self.owner = owner
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let item = object as? AVPlayerItem else { return }
if keyPath == "status" {
print("Hello")
item.removeObserver(self, forKeyPath: "status")
}
}
}
func rewindVideo(notification: Notification) {
playerLayer.player?.seek(to: .zero)
}
public func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<VideoPlayerS>) {
}
}
Swift 4 - Notification Center addObserver issue
You can improve your code with these steps:
extension Notification.Name {
static let dataDownloadCompleted = Notification.Name(
rawValue: "dataDownloadCompleted")
}
And use it like this:
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self,
selector: #selector(YourClass.sayHello),
name: .dataDownloadCompleted,
object: nil)
But as was already pointed out, issue is solved by changing to #selector
Related Topics
How to Simulate Mouse Click from MAC App to Other Application
Process Array in Parallel Using Gcd
Swiftui Set Position to Center of Different View
How to Capture Depth Data from Camera in iOS 11 and Swift 4
Difference Between String Interpolation and String Initializer in Swift
In Swift, What Does It Mean for Protocol to Inherit from Class Keyword
App Delegate Accessing Environment Object
Using Stringbyreplacingcharactersinrange in Swift
Difference Between Associated and Raw Values in Swift Enumerations
Swift Update Label (With HTML Content) Takes 1Min
How Do People Deal with Iterating a Swift Struct Value-Type Property
Checking If an Array of Custom Objects Contain a Specific Custom Object
What Does the '@' Symbol Mean in Swift
How to Calculate the 21! (21 Factorial) in Swift