Doug Beney
Doug Beney

Reputation: 320

Objective C - Asking to save before you quit

I have an Objective-C/Cocoa text-editor I'm working on(It's a mac app, not iOS).

The current challenge I'm facing is having a dialog when someone try to quit without saving.

I already have a shared bool called issavedsomewhere to tell if the user has saved or not. I even have the textview data available as a shared variable, so I can access it from any class.

I'm thinking that I'd put the save dialog in the (void)applicationWillTerminate method.

My current saving code is simple:

NSSavePanel *panel = [NSSavePanel savePanel];
    // NSInteger result;

    [panel setAllowedFileTypes:@[@"txt"]];
    [panel beginWithCompletionHandler:^(NSInteger result){

        //OK button pushed
        if (result == NSFileHandlingPanelOKButton) {
            // Close panel before handling errors
            [panel orderOut:self];
            // Do what you need to do with the selected path

            NSString *selpath = [[panel URL] path];

            NSError *error;

            BOOL didOK = [[theDATA.textvieww string]writeToFile:selpath atomically:NO encoding:NSUTF8StringEncoding error:&error];

            if(!didOK){
                //error while saving
                NSLog(@"Couldn't Save!!! -> %@", [error localizedFailureReason]);

            }else{
                //success!
                theDATA.issavedsomewhere=YES;
                theDATA.filepath=selpath;
                theDATA.filename=[[[panel URL] path] lastPathComponent];
            }

        }/*Button other than the OK button was pushed*/
        else{


        }
    }];

All it is, is an NSSavePanel that pops up and asks where you want to save.

The problem is that when I add it to (void)applicationWillTerminate, it doesn't wait for the user to answer.

Your help and ideas are appreciated:)

Upvotes: 1

Views: 572

Answers (3)

wcochran
wcochran

Reputation: 10896

Since changes to my data can happen in multiple places I simply post a "data modified" notification whenever this happens:

 [[NSNotificationCenter defaultCenter]
        postNotificationName:@"DataModifiedNotification"
                      object:self];

My app delegate has a dataSaved property and adds itself as an observer of this notification and sets its value to NO whenever the data is mutated:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {    
    self.dataSaved = YES; // set to NO when data mutated
    [[NSNotificationCenter defaultCenter] 
       addObserver:self
          selector:@selector(receiveDataModifiedNotification:)                                                     
              name:@"DataModifiedNotification"
             object:nil];
}

-(void)receiveDataModifiedNotification:(NSNotification *) notification {
     self.dataSaved = NO;
}

The the app delegate asks the user if they really want to quit to give them the opportunity to save the data (done elsewhere):

 -(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
     if (!self.dataSaved) {
          NSAlert *alert = [[NSAlert alloc] init];
          alert.alertStyle = NSAlertStyleWarning;
          alert.messageText = @"Data unsaved!";
          alert.informativeText = @"Do you really want to Quit the application?";
          [alert addButtonWithTitle:@"Quit"];
          [alert addButtonWithTitle:@"Cancel"];
          [alert beginSheetModalForWindow:self.window 
                       completionHandler:^(NSModalResponse returnCode) {
             const BOOL shouldQuit = returnCode == NSAlertFirstButtonReturn;
             [NSApp replyToApplicationShouldTerminate: shouldQuit];
           }];
           return NSTerminateLater;
     }
    return NSTerminateNow;
}

Note: Set app property NSSupportsSuddenTermination to NO which is labeled "Application can be killed immediately when user is shutting down or logging out" in Info.plist.

Upvotes: 0

mipadi
mipadi

Reputation: 410622

There are better ways to do this within the Cocoa framework, such as by using NSDocument and its ilk. However, it is possible to do what you want to do.

You first want to return NSTerminateLater in applicationShouldTerminate::

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
    if (theDATA.issavedsomewhere) {
       return NSTerminateLater;
    }
    return NSTerminateNow;
}

Then, you handler should ultimately call [NSApp replyToApplicationShouldTerminate:YES] when it is done:

NSSavePanel *panel = [NSSavePanel savePanel];
    // NSInteger result;

    [panel setAllowedFileTypes:@[@"txt"]];
    [panel beginWithCompletionHandler:^(NSInteger result){

        //OK button pushed
        if (result == NSFileHandlingPanelOKButton) {
            // Close panel before handling errors
            [panel orderOut:self];
            // Do what you need to do with the selected path

            NSString *selpath = [[panel URL] path];

            NSError *error;

            BOOL didOK = [[theDATA.textvieww string]writeToFile:selpath atomically:NO encoding:NSUTF8StringEncoding error:&error];

            if(!didOK){
                //error while saving
                NSLog(@"Couldn't Save!!! -> %@", [error localizedFailureReason]);

            }else{
                //success!
                theDATA.issavedsomewhere=YES;
                theDATA.filepath=selpath;
                theDATA.filename=[[[panel URL] path] lastPathComponent];
            }

        }/*Button other than the OK button was pushed*/
        else{


        }
        [NSApp replyToApplicationShouldTerminate:YES];
    }];

Upvotes: 1

zaph
zaph

Reputation: 112857

One possibility is to just save in a temp file and on launch check to see if the tempfile exists and perhaps ask the user if he want to use it or not.

Upvotes: 0

Related Questions