How to specify application delegate not using XCode

I have an AppleScriptObjC application written entirely “by hand” - in other words, not using XCode. I have specified the application delegate with
NSApplication’s sharedApplication’s setDelegate:me
This is placed at near the beginning of the script. In the resulting application, the menu bar’s Application Menu contains an item for Quit, but nothing happens when used. Other menu items work, such as Hide.
Many application delegate handlers are called as expected, such as applicationDidBecomeActive:, among others. However, applicationShouldTerminate: is never called and, as mentioned, the application does not quit.
The structure of the code is:

use framework "Foundation"
use framework "AppKit"
use scripting additions

NSApplication's sharedApplication's setDelegate:me

-- Application's code is here. FWIW there is no explicit run handler, although using one does not change the result.

on applicationDidBecomeActive:sender
-- Code in here is executed.
end applicationDidBecomeActive:

on applicationShouldTerminate:sender
-- Code in here is not executed, as evidenced by the fact that a NSLog() statement does not generate output.
end applicationShouldTerminate:

Perhaps I have not properly specified the application delegate. However, as I said, most delegate handlers execute normally.

I will appreciate any help.

If those other standard delegate protocol functions are being called then “me” is the delegate.

Are you sure the protocol handler
Is not being called? You may want to inject some code in there to test it like Beeping or Posting a notification.

And make sure your providing a reply to that call.

The applicationShouldTerminate:
Gets called to allow you to do any final clean up or other operations before the real quit happens. And you need to return a reply value for it of enumType NSApplicationTerminateReply

If you reply with NSTerminateLater

Your appDelegate eventually needs to call:

replyToApplicationShouldTerminate:

And provide a Boolean Value

The application delegate ‘applicationShouldTerminate:’ method should be setup like this, if you indeed want the app to terminate.

on applicationShouldTerminate:notification
	return current application's NSTerminateNow
end applicationShouldTerminate:

I use ‘notification’ but ‘sender’ also works, but be aware that the sender is an ‘NSNotification’.

Regards Mark

Mark - thank you. I do have the return statement inside the handler. For simplicity I did not include in the sample code. However, with that

NSApplication's sharedApplication's setDelegate:me

statement in the main body of the script, the handler applicationDidBecomeActive: is not called. I put an NSLog() statement inside the handler, but it did not generate any output.

AppleScriptObjC has 3 runtimes.

Script Editor, Cocoa app (made with Xcode) and Cocoa-AppleScript applet.
I guess you are talking about Script Editor and exported applets.

This AppleScript runtime have less Cocoa function and Cocoa app (made with Xcode) has all Cocoa functions such as application related events.

The middle of them is Cocoa-AppleScript applet.
You can use it via Script Editor’s File>Create new from template> Cocoa-AppleScript Applet

It has application related event capability as you like.

--
--  CocoaAppletAppDelegate.applescript
--  Cocoa-AppleScript Applet
--
--  Copyright 2011 {Your Company}. All rights reserved.
--

-- This application delegate emulates the OSA script applet by loading "main.scpt" from the
-- "Scripts" folder in the application resources and invoking the traditional run/open/reopen/quit
-- handlers in response to Cocoa application delegate methods being called.
--
-- This is provided in source form so that you may customize or replace it if your needs go
-- beyond the basic applet handlers.
--
-- Some of these methods must guard against re-entrancy, because invoking the main.scpt
-- handler may end up invoking the event handler inherited from the current application,
-- which calls the application delegate's method again.

script CocoaAppletAppDelegate
	property parent : class "NSObject"
	property mainScript : missing value -- the applet's main.scpt
	property didOpenFiles : false -- true = the application opened documents during startup
	property isOpeningFiles : false -- re-entrancy guard: true = in the process of opening files
	property isReopening : false -- re-entrancy guard: true = in the process of re-opening
	property isQuitting : false -- re-entrancy guard: true = in the process of quitting
	
	on applicationWillFinishLaunching:aNotification
		-- Insert code here to initialize your application before any files are opened 
		
		-- Emulate an OSA Applet: Load the main script from the Scripts resource folder.
		try
			set my mainScript to load script (path to resource "main.scpt" in directory "Scripts")
		on error errMsg number errNum
			-- Perhaps this should silently fail if it can't load the script; that way, a Cocoa applet
			-- can just have Cocoa classes and no main.scpt.
			display alert "Could not load main.scpt" message errMsg & " (" & errNum & ")" as critical
		end try
	end applicationWillFinishLaunching:
	
	on applicationDidFinishLaunching:aNotification
		-- Insert code here to do startup actions after your application has initialized
		
		if mainScript is missing value then return
		
		-- Emulate an OSA Applet: Invoke the "run" handler.
		
		-- If we have already opened files during startup, don't invoke the run handler.
		if didOpenFiles then return
		
		try
			tell mainScript to run
		on error errMsg number errNum
			if errNum is not -128 then
				display alert "An error occurred while running" message errMsg & " (" & errNum & ")" as critical
			end if
		end try
		
		-- TODO: Read the applet's "stay open" flag and quit if it's false or unspecified.
		-- For now, all Cocoa Applets stay open and require the run handler to explicitly quit,
		-- which is arguably more correct for a Cocoa application, anyway.
		(* if not shouldStayOpen then
			quit
		end if *)
	end applicationDidFinishLaunching:
	
	on applicationShouldHandleReopen:sender hasVisibleWindows:flag
		-- Insert code here to perform actions in response to a "reopen" event
		
		if mainScript is missing value then return true
		
		-- Guard against re-entrancy.
		if not isReopening then
			set isReopening to true
			
			-- Emulate an OSA Applet: Invoke the "reopen" handler. If there isn't one, let the application object
			-- handle reopen (this is different from an OSA applet, which would do nothing if there is no handler;
			-- this way, the application will perform the usual "create untitled document" behavior by default).
			try
				tell mainScript to reopen
				set isReopening to false
				
				return false
			on error errMsg number errNum
				if errNum is not -128 then
					display alert "An error occurred while reopening" message errMsg & " (" & errNum & ")" as critical
				end if
			end try
			
			set isReopening to false
		end if
		
		return true
	end applicationShouldHandleReopen:hasVisibleWindows:
	
	on |application|:sender openFiles:filenames
		-- Insert code here to perform actions in response to an "open documents" event
		
		-- Remember that we opened files, to avoid invoking the "run" handler later.
		set didOpenFiles to true
		
		-- Guard against re-entrancy.
		if not isOpeningFiles and mainScript is not missing value then
			set isOpeningFiles to true
			
			try
				-- Convert all the filenames from NSStrings to script strings
				set theFilenameStrings to {}
				repeat with eachFile in filenames
					set theFilenameStrings to theFilenameStrings & (eachFile as text)
				end repeat
				
				tell mainScript to open theFilenameStrings
				set isOpeningFiles to false
				
				tell sender to replyToOpenOrPrint:(current application's NSApplicationDelegateReplySuccess)
			on error errMsg number errNum
				if errNum = -128 then
					tell sender to replyToOpenOrPrint:(current application's NSApplicationDelegateReplyCancel)
				else
					display alert "An error occurred while opening file(s)" message errMsg & " (" & errNum & ")" as critical
					tell sender to replyToOpenOrPrint:(current application's NSApplicationDelegateReplyFailure)
				end if
			end try
			
			set isOpeningFiles to false
		else
			tell sender to replyToOpenOrPrint:(current application's NSApplicationDelegateReplyFailure)
		end if
	end |application|:openFiles:
	
	on applicationShouldTerminate:sender
		-- Insert code here to do any housekeeping before your application quits 
		
		-- Guard against re-entrancy.
		if not isQuitting and mainScript is not missing value then
			set isQuitting to true
			
			-- Emulate an OSA Applet: Invoke the "quit" handler; if the handler returns, it has fully
			-- handled the quit message and we should not quit, otherwise, it calls "continue quit",
			-- which returns error -10000.
			try
				tell mainScript to quit
				set isQuitting to false
				
				return current application's NSTerminateCancel
			on error errMsg number errNum
				-- -128 means there is no quit handler
				-- -10000 means the handler did "continue quit"
				if errNum is not -128 and errNum is not -10000 then
					display alert "An error occurred while quitting" message errMsg & " (" & errNum & ")" as critical
				end if
			end try
			
			set isQuitting to false
		end if
		
		return current application's NSTerminateNow
	end applicationShouldTerminate:
	
end script

3 Likes

I’ve been having troubles with NSLog generating any output as well. Which is why I mention using a BEEp of post notification

in ASOC, I always use just log:

log someString

i don’t know if there’s an issue with NSLog in ASOC, all I know is that log always works for me.

Piyomaru - Thank you for your response. I had not been aware of the Cocoa-AppleScript Applet template in Script Editor. Your explanation was very helpful.

Unfortunately, I have not been successful. Any application I created from the template will not run. macOS displays a message stating “The application X can not be run.” I have attempted numerous times to identify the source of the problem. The code I have used in main.scpt is very simple and I do not believe it to be the problem.

If you have any advice, I would appreciate it.

Regards

It is not your fault. It is Apple’s one.
I was aware of it after post.

We have to report it to Apple and tell them to fix.

Piyomaru - I appreciate your testing and letting me know. I will stop attempting to get it to work.

I have filed a bug report with Apple.

I have located an older version of CocoaAppletAppDelegate. Replacing the executable in the current version with that of the older version allows the template to function normally (as far as I have noticed.) I have attached the older executable for anyone interested.

CocoaApplet.zip (3.1 KB)

3 Likes

I’m guessing that the template isn’t used that much since there aren’t many topics about it, but my recent experience (Apple M1 running 14.7 Sonoma) is that although the Cocoa-AppleScript Applet template works, the issue seems to be when using the arm64e executable. Although an earlier version (that doesn’t have the arm64e executable) works, you can also just run it in Rosetta.

1 Like

It is a big surprise!
Cocoa-AppleScript Applet works in Rosetta!