Make an Array of URLs From a List of POSIX Paths

Is there a key for use with valueForKey which will convert an array of POSIX paths to an array of URL’s? For example,

set theFiles to {"/Users/Robert/Working/New Text File 1.txt", "/Users/Robert/Working/New Text File 2.txt", "/Users/Robert/Working/New Text File 3.txt"}
set theFiles to current application's NSArray's arrayWithArray:theFiles
set theURLs to theFiles's valueForKey:"fileURL"

1 Like

Alas, no.

My BridgePlus script library has a command for it, though:

use scripting additions
use framework "Foundation"
use script "BridgePlus"
load framework

set theResult to current application's SMSForder's URLsFrom:{path to desktop, "/Applications", "~/Documents"}

Thanks Shane.

Fredrik71. Thanks for looking at my script and for the suggestion.

I was hoping to find a solution which did not use a repeat loop, and I thought valueForKey might work, which wasn’t the case. FWIW, a solution that uses a repeat loop might be:

use framework "Foundation"
use scripting additions

set theFiles to {"/Users/Robert/Working/New Text File 1.txt", "/Users/Robert/Working/New Text File 2.txt", "/Users/Robert/Working/New Text File 3.txt"}
set theFiles to current application's NSArray's arrayWithArray:theFiles
set theURLs to current application's NSMutableArray's new()
repeat with aFile in theFiles
	set theURL to (current application's |NSURL|'s fileURLWithPath:aFile)
	(theURLs's addObject:theURL)
end repeat

theURLs

BTW, the simple solution in most cases is to write the script so that URLs rather than paths are employed. I was just curious if valueForKey might work. Thanks again.

Fredrik71. I didn’t understand the purpose of your script–thanks for the explanation.

Fredrik71. My original question was:

I tested all of your suggestions multiple times, and none of them return an array of URLs. More specifically,

  • Your script in post 4 returned the error: “[<__NSCFString 0x600002ce0480> valueForUndefinedKey:]: this class is not key value coding-compliant for the key fileURL.”

  • Your script in post 6 returned the error: “[<__NSCFString 0x600002ca27c0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key fileURL.”

  • Your script in post 8 returned true and a single URL.

So, as far as I can tell, none of your suggestions use the valueForKey method to convert an array of POSIX paths to an array of URLs.

I mainly program in Objective-C and come across needing things like this all the time. Then discovered Class Extensions. I now have my own utility framework that includes extensions for all these things. So create extension on NSArray . Create function called “arraysFileURLs”. And in the method it would run the forLoop and create the URLs, add to a mutableArray and then return the results. The programming is the same you just end up having a convenient method. It may not work with KVC. Check my next post for info on that.

 
Why do you think your original script doesn’t create an NSURL array?
 

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set theFiles to {"/Users/Robert/Working/New Text File 1.txt", "/Users/Robert/Working/New Text File 2.txt", "/Users/Robert/Working/New Text File 3.txt"}
set theFiles to current application's NSArray's arrayWithArray:theFiles
set theURLs to theFiles's valueForKey:"fileURL"

set thirdURL to theURLs's objectAtIndex:2

 
Coercion as list will return Posix files list if you need:

 

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set theFiles to {"/Users/Robert/Working/New Text File 1.txt", "/Users/Robert/Working/New Text File 2.txt", "/Users/Robert/Working/New Text File 3.txt"}
set theFiles to current application's NSArray's arrayWithArray:theFiles
set posixFiles to (theFiles's valueForKey:"fileURL") as list

 

This fails on macOS 10.14.6 Mojave…

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set theFiles to {"/Users/me/test_directory/Keyboard_Maestro_Test_Folder/A copy 1", "/Users/me/test_directory/Keyboard_Maestro_Test_Folder/A copy 2", "/Users/me/test_directory/Keyboard_Maestro_Test_Folder/ActionXML.plist"}
set theFiles to current application's NSArray's arrayWithArray:theFiles
set theURLs to theFiles's valueForKey:"fileURL"

set thirdURL to theURLs's objectAtIndex:2

You’re saying it does work on some later version of macOS? If so please specify.

It works on Catalina v10.15.7
Actually, according to the documentation, the exact syntax is to use an NSString instead of a String for the key. Try the exact syntax:
 

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set posixPaths to {"/Users/me/test_directory/Keyboard_Maestro_Test_Folder/A copy 1", "/Users/me/test_directory/Keyboard_Maestro_Test_Folder/A copy 2", "/Users/me/test_directory/Keyboard_Maestro_Test_Folder/ActionXML.plist"}
set NSStrings to current application's NSArray's arrayWithArray:posixPaths
set theURLs to NSStrings's valueForKey:(current application's NSString's stringWithString:"fileURL")

 

1 Like

No need to create an NSString.
The AppleScript to Obj-C bridge automatically converts AppleScripts strings into NSStrings

2 Likes

@KniazidisR. I ran both of the scripts in your post in Script Debugger and both returned the following error message on my Ventura computer:

[<__NSCFString 0x6000009a77c0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key fileURL.

I got the same result with the script in your post 14

1 Like

Well, if it only works on Catalina, then it might just be a fluke. Bad news.

One attempt again. Try to use “CoreFoundation” instead of “Foundation”. On my Catalina it works as well:
 

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "CoreFoundation"
use scripting additions

set posixPaths to {"/Users/me/test_directory/Keyboard_Maestro_Test_Folder/A copy 1", "/Users/me/test_directory/Keyboard_Maestro_Test_Folder/A copy 2", "/Users/me/test_directory/Keyboard_Maestro_Test_Folder/ActionXML.plist"}
set NSStrings to current application's NSArray's arrayWithArray:posixPaths
set theURLs to (NSStrings's valueForKey:(current application's NSString's stringWithString:"fileURL")) as list

 

I get the same result:

[<__NSCFString 0x600000dcb660> valueForUndefinedKey:]: this class is not key value coding-compliant for the key fileURL.

I give up. Looks like there’s some sort of bridging issue here. @Shane Stanley already suggested using Bridge Plus. Perhaps JsObjC does the job, but I only have a rudimentary knowledge of it.

Working JsObjC code can be embedded into AppleScript using osascript command. By the way, there has already been one such case. I’ll raise one of your questions that I was able to solve with JsObjC.

I think the “fileURL” method/property on NSString May only be available for newer OS Foundation Frameworks.

Shane’s bridgeplus uses a custom framework with extensions on NSString.

I’m not quite sure what “the job” is in this case, but the following snippet is working in JS/JXA/JSObjC:

const paths = ["/Users/me/test_directory/Keyboard_Maestro_Test_Folder/A copy 1", "/Users/me/test_directory/Keyboard_Maestro_Test_Folder/A copy 2", "/Users/me/test_directory/Keyboard_Maestro_Test_Folder/ActionXML.plist"];
const nsURLArray = paths.map(p => $.NSURL.fileURLWithPath($(p)));

nsURLArray is a JavaScript Array though. If you need an NSArray object, use $(nsURLArray).

As to

The main issue here is how to return something useful from JSObjC to AS. If you use osascript, there’s nothing but strings (or strings in disguise, like numbers) that you can pass back to your script. For lists, this is not overly practical (but feasible).

Back to the original question: Why not simply URL-encode the file’s path (converting spaces to %20 etc) and then prepend file:/// to the result?

Same here (of course, being on Ventura). I know very little of (AS)ObjC, but I’m wondering what to expect of valueForKey if given the parameter "fileURL" in this context. The array contains NSString objects only, and valueForKey is (if I understand correctly what KVC means) returning a property of these objects. But there is no property fileURL for NSString objects (at least not one that I can see). OTOH, there is a fileURL property for NSURL objects (which only returns true or false, though). And using that as the parameter for valueForKey on an NSArray of NSURL objects (like $(nsURLArray) in the code above) works just fine.

That would appear to require a repeat loop, in which case NSURL’s fileURLWithPath method would seem a better (or at least more commonly used) choice.

Hm. Interestingly, the following also works on Catalina.

Obviously, fileURL here is a property of the array’s NSString objects. Damn it, I can’t find the corresponding entry in the official documentation of the NSString class:
 

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set posixPaths to {"/Users/Robert/Working/New Text File 1.txt", "/Users/Robert/Working/New Text File 2.txt", "/Users/Robert/Working/New Text File 3.txt"}
set NSStrings to current application's NSArray's arrayWithArray:posixPaths
set theURLs to (NSStrings's fileURL) -- as list, optional