Every time I am trying to log out the current user (not Fast User Switchting!) I get a message from macOS (excuse me if its not exactly the message, I am getting it in german): "Unable to logout, because application "com.my.app" does not quit".
What do I need in order to avoid this? I currently have this in my AppDelegate:
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
return NSTerminateNow;
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
{
return true;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
selector: #selector(receiveLogOffOrShutdown:)
name: NSWorkspaceWillPowerOffNotification object: NULL];
}
-(void) receiveLogOffOrShutdown: (NSNotification*) note
{
[application stop:nil];
}
What I have observed for example is that receiveLogOffOrShutdown is never triggered. As well as applicationShouldTerminate never triggers a breakpoint.
Am I missing something here?
Use NSApp.terminate method
Objective C
-(void)relaunch {
int processIdentifier = NSProcessInfo.processInfo.processIdentifier;
NSString *path = [NSBundle mainBundle].executablePath;
NSArray *arg = [NSArray arrayWithObjects:[NSString stringWithFormat:#"%d",processIdentifier], nil];
[NSTask launchedTaskWithLaunchPath:path arguments:arg];
[NSApp terminate:self];
}
Swift
func relaunch() {
let processIdentifier: Int32 = NSProcessInfo.processInfo().processIdentifier
let path = NSBundle.mainBundle().executablePath! as NSString
// let myPath: String = "\(path.fileSystemRepresentation)"
NSTask.launchedTaskWithLaunchPath(path as String, arguments: ["\(processIdentifier)"])
NSApp.terminate(self)
}
func terminate(sender: AnyObject?)
When invoked, this method performs several steps to process the termination request
Hope this help you.
Related
I am facing app extension close issues , please tell me if anyone know what wrong am doing.I am using action extension after preform some action inside extension i need to return response back.
Sample Code
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
}
// With Error Case
- (void) completeActionWithError: (NSError *) error {
[self.extensionContext cancelRequestWithError: error];
}
With Success Case working fine but some time is not closing,
With Error Case not working above code.
Please let me know what went wrong.Thanks
When you create an action extension, this is the default method which will close the Action Extension View Controller:
- (IBAction)done {
// Return any edited content to the host app.
// This template doesn't do anything, so we just echo the passed in items.
[self.extensionContext completeRequestReturningItems:self.extensionContext.inputItems completionHandler:nil];
}
Since this method is already provided, you should just try calling it from your success method.
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
// Call to "done" method
[self done];
}
I have an NSDocument with some simple code:
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
self.string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return YES;
}
If I change the file in an external editor, how do I get notified of this so I can handle it? I assume there is something built in for this, but I can't find it.
I'm looking for something built into NSDocument. I'm aware of FSEvent, but that seems too low level to do something very common for most document-based apps.
Since OS X v10.7, NSDocument provides a far simpler mechanism you can override in subclasses: -presentedItemDidChange.
Handling -presentedItemDidChange, Ignoring Metadata Changes
Just relying on this callback can produce false positives, though, when metadata change. That got on my nerves quickly for files stored in Dropbox, for example.
My approach to deal with this in general, in Swift, is like this:
class MyDocument: NSDocument {
// ...
var canonicalModificationDate: Date!
override func presentedItemDidChange() {
guard fileContentsDidChange() else { return }
guard isDocumentEdited else {
DispatchQueue.main.async { self.reloadFromFile() }
return
}
DispatchQueue.main.async { self.showReloadDialog() }
}
fileprivate func showReloadDialog() {
// present alert "do you want to replace your stuff?"
}
/// - returns: `true` if the contents did change, not just the metadata.
fileprivate func fileContentsDidChange() -> Bool {
guard let fileModificationDate = fileModificationDateOnDisk()
else { return false }
return fileModificationDate > canonicalModificationDate
}
fileprivate func fileModificationDateOnDisk() -> Date? {
guard let fileURL = self.fileURL else { return nil }
let fileManager = FileManager.default
return fileManager.fileModificationDate(fileURL: fileURL)
}
}
Now you have to update the canonicalModificationDate in your subclass, too:
In a callback from the "do you want to replace contents?" alert which I call -ignoreLatestFileChanges so you don't nag your user ad infitium;
In -readFromURL:ofType:error: or however you end up reading in contents for the initial value;
In -dataOfType:error: or however you produce contents to write to disk.
You want to register with the FSEvents API. Since 10.7, you can watch arbitrary files.
Potential duplicate of this question.
When I open a document in my document-based app, edit in in another application, and switch back to my app, the same method that you mentioned (readFromData:ofType:error:) is called with the new data. This method is called when you restore a previous version from the Versions browser, too.
You could then add a boolean instance variable to check whether it's being called because of an external update (in my case, I check whether one of my IBOutlets is initialized: if it's not, the document is being loaded for the first time). You might want to move your code that makes use of the string instance variable into some method that you can call if the document is already initialized, like this:
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
self.string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (self.isLoaded)
[self documentChanged];
return YES;
}
- (void)windowControllerDidLoadNib:(FCWindowController *)windowController {
self.isLoaded = YES;
[self documentChanged];
}
- (void)documentChanged {
// use self.string as you like
]
NSMetadataQuery seems to be the best way to monitor file and folder changes without polling and with a low cpu overhead.
Some basic code for watching a folder, you'd just want to set the filePattern to the filename and not the wildcard *
NSString* filePattern = [NSString stringWithFormat:#"*"];
NSString *watchedFolder = #"not/fake/path";
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
[query setSearchScopes:#[watchedFolder]];
NSString *itemName = (NSString*)kMDItemFSName;
[query setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE %#", NSMetadataItemDisplayNameKey, filePattern]];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(queryFoundStuff:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
[nc addObserver:self selector:#selector(queryFoundStuff:) name:NSMetadataQueryDidUpdateNotification object:query];
[query setNotificationBatchingInterval:0.5];
[query startQuery];
- (void)queryFoundStuff:(NSNotification *)notification {
[query disableUpdates];
NSLog(#"Notification: %#", notification.name);
NSMutableArray *results = [NSMutableArray arrayWithCapacity:query.resultCount];
for (NSUInteger i=0; i<query.resultCount; i++) {
[results addObject:[[query resultAtIndex:i] valueForAttribute:NSMetadataItemPathKey]];
}
// file has updated, do something
[query enableUpdates];
}
I've never been able to find an ideal solution to watching files for updates, NSFilePresenter sounds like it should be the appropriate high level solution, but from what I can tell it only works if the file is being edited by another App using NSFilePresenter also. I've also tried VDKQueue and SCEvents which wrap low level kernel events but have a cpu overhead.
I'm new to Obj-c. I've got a class which sets a var boolean to YES if it's successful (Game Center login = successful), what it would be great to do, is somehow have a listener to that var that listens to when it is YES and then executes some code. Do I use a block for that? I'm also using the Sparrow framework.
Here's my code in my GameCenter.m file
-(void) setup
{
gameCenterAuthenticationComplete = NO;
if (!isGameCenterAPIAvailable()) {
// Game Center is not available.
NSLog(#"Game Center is not available.");
} else {
NSLog(#"Game Center is available.");
__weak typeof(self) weakSelf = self; // removes retain cycle error
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer]; // localPlayer is the public GKLocalPlayer
__weak GKLocalPlayer *weakPlayer = localPlayer; // removes retain cycle error
weakPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error)
{
if (viewController != nil)
{
[weakSelf showAuthenticationDialogWhenReasonable:viewController];
}
else if (weakPlayer.isAuthenticated)
{
[weakSelf authenticatedPlayer:weakPlayer];
}
else
{
[weakSelf disableGameCenter];
}
};
}
}
-(void)showAuthenticationDialogWhenReasonable:(UIViewController *)controller
{
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:controller animated:YES completion:nil];
}
-(void)authenticatedPlayer:(GKLocalPlayer *)player
{
NSLog(#"%#,%#,%#",player.playerID,player.displayName, player.alias);
gameCenterAuthenticationComplete = YES;
}
-(void)disableGameCenter
{
}
But I need to know from a different object if that gameCenterAuthenticationComplete equals YES.
You can use a delegate pattern. It's far easier to use than KVO or local notifications and it's used a lot in Obj-C.
Notifications should be used only in specific situations (e.g. when you don't know who wants to listen or when there are more than 1 listeners).
A block would work here but the delegate does exactly the same.
You could use KVO (Key-Value Observing) to watch a property of your object, but I'd rather post a NSNotification in your case.
You'll need to have the objects interested in knowing when Game Center login happened register themselves to NSNotificationCenter, then post the NSNotification in your Game Center handler. Read the Notification Programming Topics for more details !
If there is a single method to execute on a single delegate object, you can simply call it in the setter. Let me give a name to this property:
#property(nonatomic,assign, getter=isLogged) BOOL logged;
It's enough that you implement the setter:
- (void) setLogged: (BOOL) logged
{
_logged=logged;
if(logged)
[_delegate someMethod];
}
Another (suggested) way is to use NSNotificationCenter. With NSNotificationCenter you can notify multiple objects. All objects that want to execute a method when the property is changes to YES have to register:
NSNotificationCenter* center=[NSNotificationCenter defaultCenter];
[center addObserver: self selector: #selector(handleEvent:) name: #"Logged" object: nil];
The handleEvent: selector will be executed every time that logged changes to YES. So post a notification whenever the property changes:
- (void) setLogged: (BOOL) logged
{
_logged=logged;
if(logged)
{
NSNotificationCenter* center=[NSNotificationCenter defaultCenter];
[center postNotificationName: #"Logged" object: self];
}
}
Does anyone know of any class or lib that can implement autocompletion to an NSTextField?
I'am trying to get the standard autocmpletion to work but it is made as a synchronous api. I get my autocompletion words via an api call over the internet.
What have i done so far is:
- (void)controlTextDidChange:(NSNotification *)obj
{
if([obj object] == self.searchField)
{
[self.spinner startAnimation:nil];
[self.wordcompletionStore completeString:self.searchField.stringValue];
if(self.doingAutocomplete)
return;
else
{
self.doingAutocomplete = YES;
[[[obj userInfo] objectForKey:#"NSFieldEditor"] complete:nil];
}
}
}
When my store is done, i have a delegate that gets called:
- (void) completionStore:(WordcompletionStore *)store didFinishWithWords:(NSArray *)arrayOfWords
{
[self.spinner stopAnimation:nil];
self.completions = arrayOfWords;
self.doingAutocomplete = NO;
}
The code that returns the completion list to the nstextfield is:
- (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index
{
*index = -1;
return self.completions;
}
My problem is that this will always be 1 request behind and the completion list only shows on every 2nd char the user inputs.
I have tried searching google and SO like a mad man but i cant seem to find any solutions..
Any help is much appreciated.
Instead of having the boolean property doingAutocomplete, make the property your control that made the request. Let's call it autoCompleteRequestor:
#property (strong) NSControl* autoCompleteRequestor;
So where you set your current property doingAutocomplete to YES, instead store a reference to your control.
- (void)controlTextDidChange:(NSNotification *)obj
{
if([obj object] == self.searchField)
{
[self.spinner startAnimation:nil];
[self.wordcompletionStore completeString:self.searchField.stringValue];
if(self.autoCompleteRequestor)
return;
else
{
self.autoCompleteRequestor = [[obj userInfo] objectForKey:#"NSFieldEditor"];
}
}
}
Now when your web request is done, you can call complete: on your stored object.
- (void) completionStore:(WordcompletionStore *)store didFinishWithWords:(NSArray *)arrayOfWords
{
[self.spinner stopAnimation:nil];
self.completions = arrayOfWords;
if (self.autoCompleteRequestor)
{
[self.autoCompleteRequestor complete:nil];
self.autoCompleteRequestor = nil;
}
}
NSTextView has the functionality of completing words of partial words.
Take a look at the documentation for this component.
Maybe you can switch to this component in your application.
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)
}
}