3

I am using an NSAlert to display error messages on the main screen of my app. Basically, the NSAlert is a property of my main view controller

class ViewController: NSViewController {

    var alert: NSAlert?

    ...

}

And when I receive some notifications, I display some messages

func operationDidFail(notification: NSNotification)
{
    dispatch_async(dispatch_get_main_queue(), {

        self.alert = NSAlert()
        self.alert.messageText = "Operation failed"
        alert.runModal();
    })
}

Now, if I get several notifications, the alert shows up for every notification. I mean, it shows up with the first message, I click on "Ok", it disappears and then shows up again with the second message etc... Which is a normal behaviour.

What I would like to achieve is to avoid this sequence of error message. I actually only care about the first one. Is there a way to know if my alert view is currently being displayed ? Something like alert.isVisible as on iOS's UIAlertView ?

Randy
  • 4,335
  • 3
  • 30
  • 64

2 Answers2

5

From your code, I suspect that notification is triggered in background thread. In this case, any checks that alert is visible right now will not help. Your code will not start subsequent block execution until first block will finish, because runModal method will block, running NSRunLoop in modal mode.

To fix your problem, you can introduce atomic bool property and check it before dispatch_async.

Objective-C solution:

- (void)operationDidFail:(NSNotification *)note {
    if (!self.alertDispatched) {
        self.alertDispatched = YES;
        dispatch_async(dispatch_get_main_queue(), ^{
            self.alert = [NSAlert new];
            self.alert.messageText = @"Operation failed";
            [self.alert runModal];
            self.alertDispatched = NO;
        });
    }
}

Same code using Swift:

func operationDidFail(notification: NSNotification)
{
    if !self.alertDispatched {
        self.alertDispatched = true
        dispatch_async(dispatch_get_main_queue(), {
            self.alert = NSAlert()
            self.alert.messageText = "Operation failed"
            self.alert.runModal();
            self.alertDispatched = false
        })
    }
}
tbodt
  • 16,609
  • 6
  • 58
  • 83
Borys Verebskyi
  • 4,160
  • 6
  • 28
  • 42
1

Instead of run modal you could try

- beginSheetModalForWindow:completionHandler:

source: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSAlert_Class/#//apple_ref/occ/instm/NSAlert/beginSheetModalForWindow:completionHandler:

In the completion handler set the alert property to nil. And only show the alert if the alert property is nil ( which would be every first time after dismissing the alert). EDIT : I don't see the documentation say anything about any kind of flag you look for.

  • Thanks, this works as well. I accepted Boris' answer though, as I wanted to keep using `runModal`. – Randy Mar 22 '16 at 15:16