MacGapProject / MacGap2

MacGap 2
MIT License
1.2k stars 84 forks source link

Question: listening for events #44

Open davecranwell opened 9 years ago

davecranwell commented 9 years ago

I may have missed this in the docs or misunderstood, but how do I listen for events? For example if the user quits I want to be able to save work in my app before it finishes exiting.

josiaho commented 9 years ago

I am also interested in performing actions before my app finishes terminating. Is there an event for this - perhaps based off "applicationWillTerminate"? Thanks!

josiaho commented 9 years ago

Dave - here's what worked for me:

Add the following to AppDelegate.m in @implementation:

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    [self.windowController willTerminateEvent];
}

Add the following to WindowController.h: - (void) willTerminateEvent;

Add the following to WindowController.m in @interface: `- (void) willTerminateEvent;

And the following to WindowController.m in @implementation:

- (void) willTerminateEvent {
    [Event triggerEvent:@"willTerminate" forWebView:self.webView];
}

This will add a "willTerminate" event in your window:

window.addEventListener('willTerminate', function(event) {
    // Cleanup code goes here
}, true);

Example: https://dl.dropboxusercontent.com/u/222645/MacGap2-willTerminateEvent.zip

davecranwell commented 9 years ago

Nice one! thanks!

jeff-h commented 9 years ago

Does this really work? i.e. can you be sure your Javascript willTerminate handler will complete fully before the app quits?

josiaho commented 9 years ago

It seems to work in my scenario - but you're right, it probably should be based off applicationShouldTerminate with a callback to confirm app termination.

A work-around could be to remove the Quit function from the MainMenu.xib and add it under File via the MacGap Menu API:

MacGap.Menu.getItem('File').submenu().addItem('Quit', 'cmd+q', function() {
    // Clean-up code
    MacGap.terminate();
}, 1);
jeff-h commented 9 years ago

There are a bunch of different ways an app can be asked to terminate (e.g. by the system on shutdown). The Quit menu command is only one possibility.

applicationShouldTerminate sounds like the most solid solution to this, albeit more work to implement.

rawcreative commented 9 years ago

applicationWillTerminate is the last thing called before an app terminates, the only time it's not called is if the app crashes. However, this is not the method that should be used if you're trying to do some last minute work or cleanup in your app. For that, applicationShouldTerminate should be used and should return either NSTerminateCancel, NSTerminateNow, or NSTerminateLater.

This needs to be something that is implemented on an app by app basis as there's no way to shuttle responses back/forth between obj-c & js within the same method call. So it's not something that can be implemented in MacGap without impacting all apps or modifying Webkit (like atom-shell does).

josiaho commented 9 years ago

Great info - thanks!

jeff-h commented 9 years ago

Thanks @rawcreative — I recall this issue cropping up previously. Perhaps the most foolproof way in our context, given the above, is to set a flag whenever your app does something that needs cleanup, and then do that on the next app launch instead (if the flag is set).

rawcreative commented 9 years ago

Well the primary use case for using this, at least in normal apps is to trigger a data save. So setting a flag to do X next time won't really help. I experimented with a few different scenarios to try to implement this functionality but all had some sort of undesired side-effects. About the only thing that could be done is to implement a method that allows calling replyToApplicationShouldTerminate from MacGap. This way if someone wanted to implement applicationShouldTerminate, they could do so, but still be able to terminate the app from js when they are done doing whatever it is they need to do.

jeff-h commented 9 years ago

Ha, saving... makes sense. The current situation sounds risky — if the JS was busy saving something to disk and the user hit command-q too soon, I'm thinking we couldn't guarantee that the save actually completed? Is this too theoretical to worry about in real-world usage?

rawcreative commented 9 years ago

By saving I mean the app saving it's state or any unsaved data. So if the user quits the app, the app has a chance to save whatever it needs. Passing NSTerminateLater to applicationShouldTerminate suspends shutdown until you call replyToApplicationShouldTerminate, thus giving time to do whatever you need to do.

jeff-h commented 9 years ago

Yes, I understand that. But my question is, I suppose, does MG as it stands have the potential for data loss due to the app quitting halfway through JS doing something?