Folder action: unwanted re-triggering after changing file name

Indeed! Preserving the other tags is a plus ! Thanks again !

Here is a modified version which uses more ASObjC features.

use AppleScript version "2.4" -- El Capitan (10.11) or later
use framework "Foundation"
use scripting additions


property |NSURL| : a reference to current application's NSURL
property NSArray : a reference to current application's NSArray
property NSMutableArray : a reference to current application's NSMutableArray
property NSURLTagNamesKey : a reference to current application's NSURLTagNamesKey
--property NSNotFound : a reference to 9.22337203685477E+18 + 5807 -- get rid of a System bug

on adding folder items to this_folder after receiving dropped_items
	-- two instructions used to test the code
	-- set this_folder to ((path to desktop as text) & "oups:") as alias
	-- set dropped_items to {((path to desktop as text) & "oups:azer.png") as alias}
	
	set isRenamed to "isRenamed"
	tell application "Finder"
		repeat with alias_ref in dropped_items
			set {aName, anExt} to {alias_ref's name, alias_ref's name extension}
			if 10 < (system attribute "sys2") then #-- if running Yosemite (10.10)
				set aPath to POSIX path of alias_ref
				set anURL to (|NSURL|'s fileURLWithPath:aPath)
			else -- if running El Capitan (10.11) or higher
				set anURL to (NSArray's arrayWithObject:alias_ref)'s firstObject()
			end if
			-- Read the tags of the current Finder item
			set {theResult, theTags} to (anURL's getResourceValue:(specifier) ¬
				forKey:NSURLTagNamesKey |error|:(missing value))
			if theTags is missing value then
				set mutableArray to (NSMutableArray's new())
			else
				set mutableArray to (NSMutableArray's arrayWithArray:theTags)
			end if
			
			-- Check, if the current Finder item was renamed
			-- Edited according to Shane Stanley comment in message #23
			if (mutableArray's containsObject:isRenamed) as boolean then
				-- The unwanted re-triggering bring us here.
				-- Remove the isRenamed tag which is no longer needed
				(mutableArray's removeObject:isRenamed)
				(anURL's setResourceValue:mutableArray forKey:NSURLTagNamesKey |error|:(missing value))
			else
				-- As the file was not renamed, the first triggering bring us here
				-- Append isRenamed to the array of tags
				(mutableArray's addObject:isRenamed)
				-- Apply it
				(anURL's setResourceValue:mutableArray forKey:NSURLTagNamesKey |error|:(missing value))
				if anExt is not "" then
					text 1 thru -(2 + (count anExt)) of aName
					set alias_ref's name to (result & "_1." & anExt)
				else
					set alias_ref's name to (aName & "_1")
				end if
			end if
		end repeat
	end tell -- Finder
end adding folder items to

I don’t know if the instruction

property NSNotFound : a reference to 9.22337203685477E+18 + 5807 # get rid of a System bug

is required with Mojave and/or Catalina but, thank’s to Shane Stanley, I know that it is under High Sierra.

I’m too lazy to replace Finder dedicated instructions by ASObjC ones.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 24 décembre 2019 22:06:06

It is – but that’s not the end of the story.

There are a couple of issues here. The first is that the file AppleScript uses to lookup the numeric equivalent of enums has an incorrect value for NSNotFound, returning -1. This is still an issue in Catalina, and the above is an attempt to work around it.

However, even if the file had the correct value for NSNotFound, there would still be a problem with your script. That’s because NSNotFound is equivalent to the largest 64-bit integer, and AppleScript only supports 32-bit integers (and not the full range there, either).

When a number exceeds AppleScript’s integer range, it silently changes it to a real, and reals can store a much higher range of values. The problem, however, is that when you approach more than 50-odd bit integer values, the real equivalents lack enough precision, so a certain amount of rounding occurs. Because enums are used as bitmaps, that’s disastrous.

The end result is that you can use the form above to supply NSNotFound to a method if you need to, but you can’t use it to test the result of a method, because by the time any result is returned, it’s been turned into a real and rounded.

So where you say:

           set theIndex to (mutableArray's indexOfObject:isRenamed)
           if theIndex = NSNotFound then

You’re comparing to a rounded value.

You could instead use something like:

    if (theIndex > mutableArray's |count|()) then

or:

    if (mutableArray's containsObject:isRenamed) as boolean then

Thank you Shane.

I was remembering the problem with NSNotFound but I forgot the function containsObject whose use is cleaner.
I will edit the script above.

Honestly I wouldn’t be able to imagine to use

if (theIndex > mutableArray's |count|()) then

because as I knew that the old value was -1 this comparison seems to be odd.
But after all, using

if theIndex = NSNotFound then

was odd too if the script was ran under 10.12 which is supposed to return -1 as theIndex.

What is puzzling is that when I made tests, the comparison returned the wanted result and I was able to see the tag appearing then disappearing.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 25 décembre 2019 10:55:20

The act of comparison is probably forcing the right-side value to a real, and therefore making them both equally incorrect. It’s probably a safe enough comparison to make, but it’s not something I’m entirely comfortable relying on.

Thanks Shane.

Now that I re-learnt ‘containsObject’ there is no need for a scheme which is not guaranteed.
I made some new changes because I re-discovered:

set mutableArray to (NSMutableArray's new())
(mutableArray's addObject:isRenamed)

which are cleaner than

set mutableArray to (NSMutableArray's arrayWithArray:{})
(mutableArray's insertObject:isRenamed atIndex:(0))

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 25 décembre 2019 15:00:10

Keep in mind that NSMutableArray is a subclass of NSArray, which means you can use any of NSArray’s methods too. So:

set mutableArray to (NSMutableArray's arrayWithObject:isRenamed)

Bad idea Shane.
the script was supposed to add an item to an array which was allowed to contain existing items.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 26 décembre 2019 11:08:01

Hmmm. You wrote:

set mutableArray to (NSMutableArray's new())
(mutableArray's addObject:isRenamed)

That’s the same as:

set mutableArray to (NSMutableArray's arrayWithObject:isRenamed)

Oops. My fault.
You where fooled by the way I presented the instructions.

I would have posted


# instruction 1 - supposed to create a new array
set mutableArray to (NSMutableArray's new())
# instruction 2 - supposed to append an item to an other array which may already contain items
(mutableArray's addObject:isRenamed)

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 26 décembre 2019 13:54:39