I would like my app to be notified when the user inserts an SD card. I tried adding an observer, but no notifications are received.
class SDCardWatcher: NSObject {
public override init() {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(self.onDiskMounted(_:)), name: .NSWorkspaceDidMount, object: nil)
}
#objc public func onDiskMounted(_ notification : Notification) {
// This code is never executed
print(notification.description)
}
}
I am new to MacOS so would appreciate any suggestions.
Related
In one of the view controller files in my project, there are two functions, one is called in viewdidload and another is called by Notification and observers. Those functions do exactly the same thing, and I was wondering if I get rid of one of the functions, especially the one without using #objc in front. (otherwise I get an error)
override func viewDidLoad() {
super.viewDidLoad()
configureNotifications()
displayItems()
}
func displayItems() {
fetchLiveEvents { [weak self] in
self?.applySnapshot(animatingDifferences: true)
}
}
func configureNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(updateExistingItem), name: .updateExistingItem, object: nil)
}
#objc func updateExistingItem() {
fetchLiveEvents { [weak self] in
self.applySnapshot(animatingDifferences: true)
}
}
Since I'm using the notification canter, I cannot get rid of #objc in front of updateExistingItem function. However, the updateExistingItem and displayItems are doing exactly something, so I feel it's kinda redundant and I was thinking to get rid of displayItems function from the viewDidLoad and call updateExistingItem (probably change the name) in viewdidLoad instead.
Is there any convention in Swift programming that keeps both #objc and normal function when they are doing the same thing? or is it just a personal preference and doesn't matter to leave both of them?
viewDidLoad just call once when the screen is present if you go to another screen by pushing a viewcontroller or presenting a controller and comeback to this controller the viewDidLoad didn't triggered it will never called again until the next run / terminate the app and open again.
so your function is called by the notification to run again when this screen appear.
// just called once
override func viewDidLoad() {
super.viewDidLoad()
configureNotifications()
displayItems()
}
// just called every time when you popped a viewController or tap on tab bar items using tabbar controller
override func viewWillAppear() {
super.viewDidLoad()
displayItems()
}
in your scenario may be you came back to this screen by present some other screen and do some functionality there and call the notification to be trigger on this screen so nothing will trigger if your present a screen by modal presentation style over full screen
That's why called the notification to start displaying item again
override func viewDidLoad() {
super.viewDidLoad()
configureNotifications()
displayItems()
}
// called once
func displayItems() {
fetchLiveEvents { [weak self] in
self?.applySnapshot(animatingDifferences: true)
}
}
func configureNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(updateExistingItem), name: .updateExistingItem, object: nil)
}
// called every time when you trigger notifcation
#objc func updateExistingItem() {
fetchLiveEvents { [weak self] in
self.applySnapshot(animatingDifferences: true)
}
}
I am building an iOS app using the new language Swift. Now it is an HTML5 app, that displays HTML content using the UIWebView. The app has local notifications, and what i want to do is trigger a specific javascript method in the UIWebView when the app enters foreground by clicking (touching) the local notification.
I have had a look at this question, but it does not seem to solve my problem. I have also come across this question which tells me about using UIApplicationState, which is good as that would help me know the the app enters foreground from a notification. But when the app resumes and how do i invoke a method in the viewController of the view that gets displayed when the app resumes?
What i would like to do is get an instance of my ViewController and set a property in it to true. Something as follows
class FirstViewController: UIViewController,UIWebViewDelegate {
var execute:Bool = false;
#IBOutlet var tasksView: UIWebView!
}
And in my AppDelegate i have the method
func applicationWillEnterForeground(application: UIApplication!) {
let viewController = self.window!.rootViewController;
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var setViewController = mainStoryboard.instantiateViewControllerWithIdentifier("FirstView") as FirstViewController
setViewController.execute = true;
}
so what i would like to do is when the app enters foreground again, i want to look at the execute variable and run the method as follows,
if execute{
tasksView.stringByEvaluatingJavaScriptFromString("document.getElementById('sample').click()");
}
Where should i put the code for the logic to trigger the javascript from the webview? would it be on viewDidLoad method, or one of the webView delegate methods? i have tried to put that code in the viewDidLoad method but the value of the boolean execute is set to its initial value and not the value set in the delegate when the app enters foreground.
If I want a view controller to be notified when the app is brought back to the foreground, I might just register for the UIApplication.willEnterForegroundNotification notification (bypassing the app delegate method entirely):
class ViewController: UIViewController {
private var observer: NSObjectProtocol?
override func viewDidLoad() {
super.viewDidLoad()
observer = NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { [unowned self] notification in
// do whatever you want when the app is brought back to the foreground
}
}
deinit {
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
}
}
Note, in the completion closure, I include [unowned self] to avoid strong reference cycle that prevents the view controller from being deallocated if you happen to reference self inside the block (which you presumably will need to do if you're going to be updating a class variable or do practically anything interesting).
Also note that I remove the observer even though a casual reading of the removeObserver documentation might lead one to conclude is unnecessary:
If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method.
But, when using this block-based rendition, you really do need to remove the notification center observer. As the documentation for addObserver(forName:object:queue:using:) says:
To unregister observations, you pass the object returned by this method to removeObserver(_:). You must invoke removeObserver(_:) or removeObserver(_:name:object:) before any object specified by addObserver(forName:object:queue:using:) is deallocated.
I like to use the Publisher initializer of NotificationCenter. Using that you can subscribe to any NSNotification using Combine.
import UIKit
import Combine
class MyFunkyViewController: UIViewController {
/// The cancel bag containing all the subscriptions.
private var cancelBag: Set<AnyCancellable> = []
override func viewDidLoad() {
super.viewDidLoad()
addSubscribers()
}
/// Adds all the subscribers.
private func addSubscribers() {
NotificationCenter
.Publisher(center: .default,
name: UIApplication.willEnterForegroundNotification)
.sink { [weak self] _ in
self?.doSomething()
}
.store(in: &cancelBag)
}
/// Called when entering foreground.
private func doSomething() {
print("Hello foreground!")
}
}
Add Below Code in ViewController
override func viewDidLoad() {
super.viewDidLoad()
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector:#selector(appMovedToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func appMovedToForeground() {
print("App moved to foreground!")
}
In Swift 3, it replaces and generates the following.
override func viewDidLoad() {
super.viewDidLoad()
foregroundNotification = NotificationCenter.default.addObserver(forName:
NSNotification.Name.UIApplicationWillEnterForeground, object: nil, queue: OperationQueue.main) {
[unowned self] notification in
// do whatever you want when the app is brought back to the foreground
}
The simple Swift 4 example below should stop when the computer's display goes to sleep.
class Observer {
var asleep = false
func addDNC () {
NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.screensDidSleepNotification, object: nil, queue: nil, using: notificationRecieved)
}
func notificationRecieved (n: Notification) {
asleep = true
}
}
let observer = Observer ()
observer.addDNC ()
while (!observer.asleep) {}
print ("zzzz")
However, the program gets stuck in the while loop. What am I doing wrong, and what is the proper way to wait for a Notification?
I have tried using a selector (#selector (notificationRecieved), with #objc in the function declaration, of course), to no avail.
Start a template app in Xcode and modify the ViewController.swift to do this:
import Cocoa
class Observer {
var asleep = false
func addDNC () {
NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.screensDidSleepNotification, object: nil, queue: nil, using: notificationRecieved)
}
func notificationRecieved (n: Notification) {
print("got sleep notification!")
asleep = true
}
}
class ViewController: NSViewController {
let observer = Observer ()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
observer.addDNC ()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
The difference between your code and mine is that I'm not doing the wacky sleepy polling thing you're doing (that's going to lead to a spinning pizza cursor), and I'm also setting observer to be a property off the ViewController object, so the observer property sticks around as long as the view controller does.
My code:
overide func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "activeAgain", name: UIApplicationWillEnterForegroundNotification, object:nil)
}
func activeAgain() {
println("ActiveAgain") //Comes immediately
callSeparateFunction() //Takes 5s
}
Why does the println comes immediately and the separate function call takes up to 5s? I have to call a new function when the app comes back to foreground. Thanks!
I have a view controller with a button. When the button is pressed it adds an observer, like so:
func buttonPress(sender:UIButton){
NSNotificationCenter.defaultCenter().addObserverForName("buttonPressEvent", object:nil, queue:nil, usingBlock:{(notif) -> Void in
// code
})
}
When I dismiss this view controller, and then return to it and press the button the //code is executed twice. If I go away and come back again the //code is executed three times, and so on.
What I want to do is to remove the Observer before I add it again, so this code doesn't execute twice. Ive gone through the documentation here and Ive added this line of code just above where I add the Observer:
NSNotificationCenter.defaultCenter().removeObserver(self, name:"buttonPressEvent", object:nil)
But this isnt working.
Can anyone tell me where I'm going wrong?
When you use the 'blocks' based approach to observing notifications then self isn't in fact the observer. The function returns an object which acts as the observer:
func addObserverForName(_ name: String?,
object obj: AnyObject?,
queue queue: NSOperationQueue?,
usingBlock block: (NSNotification!) -> Void) -> NSObjectProtocol
You need to keep a reference to this returned object and pass it in as the observer when you call removeObserver
It's explained well in the Apple Doc here
Implemented it like this, seems to be working fine.
override func viewDidLoad()
{
super.viewDidLoad()
AddScreenShotNotification()
}
func AddScreenShotNotification() {
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(MyViewController.ScreenShotTaken),
name: UIApplicationUserDidTakeScreenshotNotification,
object: nil)
}
func ScreenShotTaken()
{
// do something
}
override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self)
}