Detect in MacOS opening/closure laptops's lid [duplicate] - objective-c

I am developing an app in Xcode on Mac and would like to know the event which is fired when the mac gets back from sleep.
AwakeFromNib doesn't seem to work.

For swift 3:
func onWakeNote(note: NSNotification) {
print("Received wake note: \(note.name)")
}
func onSleepNote(note: NSNotification) {
print("Received sleep note: \(note.name)")
}
func fileNotifications() {
NSWorkspace.shared().notificationCenter.addObserver(
self, selector: #selector(onWakeNote(note:)),
name: Notification.Name.NSWorkspaceDidWake, object: nil)
NSWorkspace.shared().notificationCenter.addObserver(
self, selector: #selector(onSleepNote(note:)),
name: Notification.Name.NSWorkspaceWillSleep, object: nil)
}
For swift 4:
#objc func onWakeNote(note: NSNotification) {
...
}
#objc func onSleepNote(note: NSNotification) {
...
}
func fileNotifications() {
NSWorkspace.shared.notificationCenter.addObserver(
self, selector: #selector(onWakeNote(note:)),
name: NSWorkspace.didWakeNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(
self, selector: #selector(onSleepNote(note:)),
name: NSWorkspace.willSleepNotification, object: nil)
}

Just found it:
- (void) receiveWakeNote: (NSNotification*) note
{
NSLog(#"receiveSleepNote: %#", [note name]);
}
- (void) fileNotifications
{
//These notifications are filed on NSWorkspace's notification center, not the default
// notification center. You will not receive sleep/wake notifications if you file
//with the default notification center.
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
selector: #selector(receiveWakeNote:)
name: NSWorkspaceDidWakeNotification object: NULL];
}

You can use IORegisterForSystemPower().
Connects the caller to the Root Power Domain IOService for the purpose
of receiving sleep & wake notifications for the system. Does not
provide system shutdown and restart notifications.
io_connect_t IORegisterForSystemPower (
void *refcon,
IONotificationPortRef *thePortRef,
IOServiceInterestCallback callback,
io_object_t *notifier ) ;
Take a look at Q:How can my application get notified when the computer is going to sleep or waking from sleep? How to I prevent sleep?

Related

How to access NSApplicationLaunchUserNotificationKey in Swift?

I am trying to translate the following from Objective-C to Swift, but I'm stuck!
See https://developer.apple.com/documentation/appkit/nsapplicationlaunchusernotificationkey
NSUserNotification *userNotification = [[myNotification userInfo]
objectForKey:NSApplicationLaunchUserNotificationKey];
if (userNotification) {
// The app was launched by a user selection from Notification Center.
}
The objective-c syntax in your question is fine.
But the Notification it refers to is the one belonging to -applicationDidFinishLaunching:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSUserNotification *userNotification = aNotification.userInfo[NSApplicationLaunchUserNotificationKey];
if (userNotification) {
// The app was launched by a user selection from Notification Center.
}
}
but the API is deprecated.
So it in swift it will look like..
func applicationDidFinishLaunching(_ notification: Notification)
if let response = notification.userInfo?[NSApplication.launchUserNotificationUserInfoKey] as? UNNotificationResponse {
// evaluate response.notification.request.content
}
}

How to use Objective C selectors as arguments when subscribing to NSNotifications? [duplicate]

This question already has answers here:
How to write Keyboard notifications in Swift 3
(12 answers)
Closed 6 years ago.
I have the following subscription in swift. I need to know when the keyboard will show to move the view up. It compiles and works as expected but I don't know how to get rid of this warning. "No method declared with Objective-C selector'keyboardWillShow'"
// Subscribing class to recieve the notification
func subscribeToKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow", name: UIKeyboardWillShowNotification, object: nil)
}
// Shifting view's frame up
func keyboardWillShow(notification: NSNotification) {
view.frame.origin.y -= getKeyboardHeight(notification)
}
// Getting keyboard height
func getKeyboardHeight(notification: NSNotification) -> CGFloat {
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue // of CGRect
return keyboardSize.CGRectValue().height
}
Thanks in advance for any suggestions!
Use #selector:
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIKeyboardWillShowNotification, object: nil)

Apple watch communicate with iOS app a new swift project vs to existing obj c project

1) I've managed to communicate from apple watch to iOS app using a complete swift project (iOS app in swift and Apple Watch target in swift)
Code:
In InterfaceController.swift
#IBAction func buttonPressed() {
let watchMessage = ["SiteName" : "Tech-recipes"]
WKInterfaceController.openParentApplication(watchMessage, reply: { (reply:[NSObject : AnyObject]!, error: NSError!) -> Void in
if let message = reply["Message"] as? String{
println(message)
}
})
}
in AppDelegate.swift
func application(application: UIApplication!, handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!, reply: (([NSObject : AnyObject]!) -> Void)!) {
if let siteName = userInfo["SiteName"] as? String{
reply(["Message":"Hello from \(siteName)"])
}
}
Output: "Hello from Tech-recipes"
2) However, when I want to integrate the apple watch target in swift to the exiting project in obj-c, it will crash and give me this error: "fatal error: unexpectedly found nil while unwrapping an Optional value"
In InterfaceController.swift
#IBAction func buttonPressed() {
let watchMessage = ["SiteName" : "Tech-recipes"]
WKInterfaceController.openParentApplication(watchMessage, reply: { (reply:[NSObject : AnyObject]!, error: NSError!) -> Void in
if let message = reply["Message"] as? String{ //CRASH HERE!!!!
println(message)
}
})
}
iPhoneAppDelegate.m
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply{
// Performs task on phone here
// Sending back a reply
if ([[userInfo valueForKey:#"SiteName"] length]>0) {
//NSMutableDictionary *reply = [[NSMutableDictionary alloc] init];
[reply setValue:[NSString stringWithFormat:#"Hello from %#", [userInfo valueForKey:#"SiteName"]] forKey:#"Message"];
}
}
Update:
--> Noticed that in handleWatchKitExtensionRequest: method the type for userInfo is different in swift and obj-c, in [NSObject : AnyObject]! and NSDictionary respectively. How to solve this?
--> Got an error in error: NSError!: [0] (null) #"NSLocalizedDescription" : #"The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]"
Solved it!
Missed out the reply(aNsdictionary) in the app delegate

Detect when a volume is mounted on OS X

I have an OS X application that needs to respond to a volume being mounted or unmounted.
I've already solved this problem by retrieving the list of volumes periodically and checking for changes, but I'd like to know if there is a better way.
Register to the notification center you get from [[NSWorkspace sharedWorkspace] notificationCenter] and then process the notifications you are interested in. These are the volume related ones: NSWorkspaceDidRenameVolumeNotification, NSWorkspaceDidMountNotification, NSWorkspaceWillUnmountNotification and NSWorkspaceDidUnmountNotification.
The NSWorkspace approach is exactly the kind of thing I was looking for. A few lines of code later, I have a much better solution than using a timer.
-(void) monitorVolumes
{
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: #selector(volumesChanged:) name:NSWorkspaceDidMountNotification object: nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: #selector(volumesChanged:) name:NSWorkspaceDidUnmountNotification object:nil];
}
-(void) volumesChanged: (NSNotification*) notification
{
NSLog(#"dostuff");
}
Swift 4 version:
Declare NSWorkspace in applicationDidFinishLaunching and add observers for mount and unmount events.
let workspace = NSWorkspace.shared
workspace.notificationCenter.addObserver(self, selector: #selector(didMount(_:)), name: NSWorkspace.didMountNotification, object: nil)
workspace.notificationCenter.addObserver(self, selector: #selector(didUnMount(_:)), name: NSWorkspace.didUnmountNotification, object: nil)
Capture mount and unmount events in:
#objc func didMount(_ notification: NSNotification) {
if let devicePath = notification.userInfo!["NSDevicePath"] as? String {
print(devicePath)
}
}
#objc func didUnMount(_ notification: NSNotification) {
if let devicePath = notification.userInfo!["NSDevicePath"] as? String {
print(devicePath)
}
}
It will print device path e.g /Volumes/EOS_DIGITAL
Here are the constants you can read from userInfo.
NSDevicePath,
NSWorkspaceVolumeLocalizedNameKey
NSWorkspaceVolumeURLKey
Do you know SCEvents? It allows you to be notified when the contents of an observed folder change (/Volumes). This way you don't have to use a timer to periodically check the contents.

How to pass object with NSNotificationCenter

I am trying to pass an object from my app delegate to a notification receiver in another class.
I want to pass integer messageTotal. Right now I have:
In Receiver:
- (void) receiveTestNotification:(NSNotification *) notification
{
if ([[notification name] isEqualToString:#"TestNotification"])
NSLog (#"Successfully received the test notification!");
}
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismissSheet) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveTestNotification:) name:#"eRXReceived" object:nil];
In the class that is doing the notification:
[UIApplication sharedApplication].applicationIconBadgeNumber = messageTotal;
[[NSNotificationCenter defaultCenter] postNotificationName:#"eRXReceived" object:self];
But I want to pass the object messageTotal to the other class.
You'll have to use the "userInfo" variant and pass a NSDictionary object that contains the messageTotal integer:
NSDictionary* userInfo = #{#"total": #(messageTotal)};
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:#"eRXReceived" object:self userInfo:userInfo];
On the receiving end you can access the userInfo dictionary as follows:
-(void) receiveTestNotification:(NSNotification*)notification
{
if ([notification.name isEqualToString:#"TestNotification"])
{
NSDictionary* userInfo = notification.userInfo;
NSNumber* total = (NSNumber*)userInfo[#"total"];
NSLog (#"Successfully received test notification! %i", total.intValue);
}
}
Building on the solution provided I thought it might be helpful to show an example passing your own custom data object (which I've referenced here as 'message' as per question).
Class A (sender):
YourDataObject *message = [[YourDataObject alloc] init];
// set your message properties
NSDictionary *dict = [NSDictionary dictionaryWithObject:message forKey:#"message"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationMessageEvent" object:nil userInfo:dict];
Class B (receiver):
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:#selector(triggerAction:) name:#"NotificationMessageEvent" object:nil];
}
#pragma mark - Notification
-(void) triggerAction:(NSNotification *) notification
{
NSDictionary *dict = notification.userInfo;
YourDataObject *message = [dict valueForKey:#"message"];
if (message != nil) {
// do stuff here with your message data
}
}
Swift 5
func post() {
NotificationCenter.default.post(name: Notification.Name("SomeNotificationName"),
object: nil,
userInfo:["key0": "value", "key1": 1234])
}
func addObservers() {
NotificationCenter.default.addObserver(self,
selector: #selector(someMethod),
name: Notification.Name("SomeNotificationName"),
object: nil)
}
#objc func someMethod(_ notification: Notification) {
let info0 = notification.userInfo?["key0"]
let info1 = notification.userInfo?["key1"]
}
Bonus (that you should definitely do!) :
Replace Notification.Name("SomeNotificationName") with .someNotificationName:
extension Notification.Name {
static let someNotificationName = Notification.Name("SomeNotificationName")
}
Replace "key0" and "key1" with Notification.Key.key0 and Notification.Key.key1:
extension Notification {
enum Key: String {
case key0
case key1
}
}
Why should I definitely do this ? To avoid costly typo errors, enjoy renaming, enjoy find usage etc...
Swift 2 Version
As #Johan Karlsson pointed out... I was doing it wrong. Here's the proper way to send and receive information with NSNotificationCenter.
First, we look at the initializer for postNotificationName:
init(name name: String,
object object: AnyObject?,
userInfo userInfo: [NSObject : AnyObject]?)
source
We'll be passing our information using the userInfo param. The [NSObject : AnyObject] type is a hold-over from Objective-C. So, in Swift land, all we need to do is pass in a Swift dictionary that has keys that are derived from NSObject and values which can be AnyObject.
With that knowledge we create a dictionary which we'll pass into the object parameter:
var userInfo = [String:String]()
userInfo["UserName"] = "Dan"
userInfo["Something"] = "Could be any object including a custom Type."
Then we pass the dictionary into our object parameter.
Sender
NSNotificationCenter.defaultCenter()
.postNotificationName("myCustomId", object: nil, userInfo: userInfo)
Receiver Class
First we need to make sure our class is observing for the notification
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("btnClicked:"), name: "myCustomId", object: nil)
}
Then we can receive our dictionary:
func btnClicked(notification: NSNotification) {
let userInfo : [String:String!] = notification.userInfo as! [String:String!]
let name = userInfo["UserName"]
print(name)
}
Swift 5.1 Custom Object/Type
// MARK: - NotificationName
// Extending notification name to avoid string errors.
extension Notification.Name {
static let yourNotificationName = Notification.Name("yourNotificationName")
}
// MARK: - CustomObject
class YourCustomObject {
// Any stuffs you would like to set in your custom object as always.
init() {}
}
// MARK: - Notification Sender Class
class NotificatioSenderClass {
// Just grab the content of this function and put it to your function responsible for triggering a notification.
func postNotification(){
// Note: - This is the important part pass your object instance as object parameter.
let yourObjectInstance = YourCustomObject()
NotificationCenter.default.post(name: .yourNotificationName, object: yourObjectInstance)
}
}
// MARK: -Notification Receiver class
class NotificationReceiverClass: UIViewController {
// MARK: - ViewController Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Register your notification listener
NotificationCenter.default.addObserver(self, selector: #selector(didReceiveNotificationWithCustomObject), name: .yourNotificationName, object: nil)
}
// MARK: - Helpers
#objc private func didReceiveNotificationWithCustomObject(notification: Notification){
// Important: - Grab your custom object here by casting the notification object.
guard let yourPassedObject = notification.object as? YourCustomObject else {return}
// That's it now you can use your custom object
//
//
}
// MARK: - Deinit
deinit {
// Save your memory by releasing notification listener
NotificationCenter.default.removeObserver(self, name: .yourNotificationName, object: nil)
}
}