FilePicker

A FilePicker is the user-facing system interface for selecting files or folders. The items chosen by the user are returned to the Omni Automation script as an array of URLs to the chosen items. The FilePicker class is supported by all Omni Automation applications on both iOS and macOS.

Constructors

An instance of the FilePicker class is created using the standard new constructor without any initial provided parameters.

New FilePicker Instance


picker = new FilePicker()

Instance Properties

Control of the selection options for a FilePicker instance is accomplished by setting the values of the following properties:

For example, the following script will create a FilePicker instance that will only allow the selection of a single text file:

Set Properties of New FilePicker


picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText]

Instance Methods

Once an instance of the FilePicker class has been created, it can be displayed to the user by calling the show() method on the instance.

Create and Display FilePicker


picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] pickerPromise = picker.show()

Depending upon which operating system is used by the device running the script, the file picker will be presented as either a sheet (macOS 10.5) or an overlay dialog (iOS, macOS 11+).

Alternate Plain Text Type


aType = new FileType("public.plain-text") picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [aType] pickerPromise = picker.show()

Processing the FilePicker Results

When a file picker dialog is displayed, a JavaScript Promise object is returned to the script. This promise represents either the completion or cancellation of the user’s interaction with the file picker dialog, and if successful will contain an array of URL references to the items chosen by the user.

The Promise object can be processed by the script with standalone functions that process its success or cancelation, or through the inline appending of the then(…) function of the Promise class to the show() command (of the FilePicker class) to act as a receiver for the passed promise object.

In the following example of the inline technique, the call-back function within the then(…) method is passed the result of the successful completion of the picker dialog, which is an array of URLs to the chosen items. Note that the successful picker result is always an array of URL objects even if only a single item was chosen.

Choose and Import Text from File


picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] picker.show().then(urlsArray => { aURL = urlsArray[0] aURL.fetch(data => { console.log(data.toString()) }) })

Optionally, you may call the then() function on the stored Promise object to retrieve the chosen file(s), and to respond with other actions should the user choose to cancel the FilePicker:

FilePicker (Explicit Promise)


picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] pickerPromise = picker.show() pickerPromise.then(function(urlsArray){ fileURL = urlsArray[0] console.log(fileURL.string) }) pickerPromise.catch(err =>{ console.log("picker cancelled", err.message) })

Another approach is wrap the FilePicker code in an asynchronous function (async) and precede the call to show the FilePicker with the term: await

FilePicker (Asynchronous Promise)


(async () => { try { picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] fileURL = await picker.show() console.log(fileURL.string) } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } })();

Here’s an example of using a file picker to select an open an existing OmniGraffle file and add a new canvas to it:

omnigraffle://localhost/omnijs-run?script=try%7Bvar%20picker%20%3D%20new%20FilePicker%28%29%0Apicker%2Efolders%20%3D%20false%0Apicker%2Emultiple%20%3D%20false%0AaFileType%20%3D%20new%20FileType%28%22com%2Eomnigroup%2Eomnigraffle%2Egraffle%22%29%0Apicker%2Etypes%20%3D%20%5BaFileType%5D%0ApickerPromise%20%3D%20picker%2Eshow%28%29%0A%0ApickerPromise%2Ethen%28function%28urlsArray%29%7B%0A%09fileURL%20%3D%20urlsArray%5B0%5D%0A%09app%2EopenDocument%28%0A%09%09document%2C%0A%09%09fileURL%2C%0A%09%09function%28doc%2CwasOpen%29%7B%0A%09%09%09cnvs%20%3D%20doc%2Eportfolio%2EaddCanvas%28%29%0A%09%09%7D%29%0A%7D%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Choose and Open OmniGraffle Document
 

var picker = new FilePicker() picker.folders = false picker.multiple = false aFileType = new FileType("com.omnigroup.omnigraffle.graffle") picker.types = [aFileType] pickerPromise = picker.show() pickerPromise.then(function(urlsArray){ fileURL = urlsArray[0] app.openDocument( document, fileURL, function(doc,wasOpen){ cnvs = doc.portfolio.addCanvas() } ) })

 01  Create a new empty file picker object and store it in a variable.

 02-03  Set the properties of the new file picker.

 04  Create a new FileType instance of an OmniGraffle document.

 05  Assign the created file type as the value of the picker’s type property.

 06  Use the show(…) method of the FilePicker class to display the file picker dialog to the user. The result of this method is a new JavaScript Promise object that is stored in the variable: pickerPromise

 08-17  The successful promise is processed using the then(…) method whose call-back function is automatically passed an array of the chosen item URLs.

 09  A URL object is extracted from the URLs array passed by the promise as input to the call-back function.

 10-16  Use the openDocument(…) function of the Application class (passing in the URL to the chosen file) to open the file.

 13-15  The call-back function is passed as input a reference to the opened document.

 14  The document’s portfolio property is used to reference the document’s Portfolio object for calling the addCanvas() method in order to create a new canvas.

An Alternative Method

JavaScript also offers the ability to execute the FilePicker asynchronously, meaning that the statement that invokes the FilePicker can wait until the user has completed selecting one or more files before continuing in the host script. For this to occur, the FilePicker code must be placed within an asynchronous function (marked with the term async) with the FilePicker call preceded with the term: await The resulting array of URLs will be returned as the result of the statement.

Here is an asynchronous version of the previous script example:

The FilePicker (Asynchronous)


(async () => { try { picker = new FilePicker() picker.folders = false picker.multiple = false aFileType = new FileType("com.omnigroup.omnigraffle.graffle") picker.types = [aFileType] urlsArray = await picker.show() fileURL = urlsArray[0] app.openDocument( document, fileURL, function(doc,wasOpen){ cnvs = doc.portfolio.addCanvas() } ) } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } })();

Create Image Data URL

This script example will create an HTML image data URL using the image data of the chosen PNG file. Image Data URLs do not link to files, but instead include the image data expressed as a base64 string.

The resulting image data URL will be logged to the console and copied to the clipboard, ready to paste into HTML content. IMPORTANT: Always choose VERY SMALL images, and be sure to set the values of the image width and height style properties!

<-- Here is an image displayed using an image data URL!

picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [TypeIdentifier.png] picker.show().then(function(urlsArray){ aURL = urlsArray[0] aURL.fetch(function(data){ var imageURL = "" console.log(imageURL) Pasteboard.general.string = imageURL new Alert("COMPLETED","URL code is on the clipboard.").show() }) })
Create Image Data URL from Chosen Image
  

picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [TypeIdentifier.png] picker.show().then(function(urlsArray){ aURL = urlsArray[0] aURL.fetch(function(data){ imageURL = "<img style=\"width:auto;height:auto;\" src=\"data:image/png;base64," + data.toBase64() + "\">" console.log(imageURL) Pasteboard.general.string = imageURL new Alert("COMPLETED","URL code is on the clipboard.").show() }) })

Folder Picker

To create a FilePicker that selects a chosen folder, include the UTI for a folder AND set the value of the picker’s folders property to true. Here is an asynchronous example script that opens the selected folder:

Folder Picker


(async () => { try { folderUTI = new TypeIdentifier("public.folder") picker = new FilePicker() picker.multiple = false picker.folders = true picker.types = [folderUTI] urls = await picker.show() urls[0].open() } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } })();

Using FilePicker with Plug-Ins

The use of a file picker can greatly enhance the usefulness of Omni Automation actions designed to import and integrate data or files into documents. For example, a file picker provides an excellent way for a user to select a text file whose contents are to be imported into a selected container or shape in OmniGraffle.

You can use the following example action as a template for integration of a file picker into an Omni Automation action.

Import Text into Shape
  

/*{ "type": "action", "targets": ["omnigraffle"], "author": "Otto Automator", "version": "1.0", "identifier": "com.omni-automation.og.import-text-into-shape", "description": "Imports text from the chosen text file into the selected shape.", "label": "Import Text into Shape", "shortLabel": "Import Text", "paletteLabel": "Import Text", "image": "square.and.arrow.down.fill" }*/ (() => { const action = new PlugIn.Action(async function(selection, sender){ selectedShape = selection.solids[0] // CONSTRUCT THE PICKER picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] // SHOW THE PICKER AND RETURN JAVASCRIPT PROMISE urlsArray = await picker.show() // PROCESS THE PICKER RESULTS fileURL = urlsArray[0] // IMPORT DATA FROM CHOSEN FILE fileURL.fetch(function(data) { selectedShape.text = data.toString() }) }) // VALIDATE DOCUMENT SELECTION action.validate = function(selection, sender){ return (selection.solids.length == 1 ? true:false) } return action })();

Picker Prompt

In recent Omni application updates, the message property has been added to the FilePicker class to provide the ability to display a prompt in the macOS file picker dialog. Since iOS does not support the inclusion of a prompt in the iOS file picker, the following script example shows how to use an alert to provide similar prompt functionality on iOS:

omnigraffle://localhost/omnijs-run?script=try%7Bvar%20picker%20%3D%20new%20FilePicker%28%29%0Apicker%2Efolders%20%3D%20false%0Apicker%2Emultiple%20%3D%20false%0Apicker%2Etypes%20%3D%20%5BFileType%2EplainText%5D%0A%2F%2F%20DECLARE%20THE%20PROMISE%20VARIABLE%0Avar%20pickerPromise%0A%2F%2F%20CHECK%20PLATFORM%0Aif%28app%2EplatformName%20%3D%3D%3D%20%22iOS%22%29%7B%0A%09msg%20%3D%20%22Tap%20%E2%80%9DContinue%E2%80%9D%20and%20choose%20a%20text%20file%20from%20the%20forthcoming%20file%20picker%3A%22%0A%09var%20alert%20%3D%20new%20Alert%28%22Select%20File%22%2C%20msg%29%0A%09alert%2EaddOption%28%22Continue%22%29%0A%09alert%2Eshow%28function%28result%29%7B%0A%09%09pickerPromise%20%3D%20picker%2Eshow%28%29%0A%09%7D%29%0A%7D%20else%20if%20%28app%2EplatformName%20%3D%3D%3D%20%22macOS%22%29%7B%0A%09picker%2Emessage%20%3D%20%22Choose%20the%20text%20file%20to%20import%3A%22%0A%09pickerPromise%20%3D%20picker%2Eshow%28%29%0A%7D%0A%2F%2F%20READ%20CONTENTS%20OF%20THE%20CHOSEN%20TEXT%20FILE%0ApickerPromise%2Ethen%28function%28urls%29%7B%0A%09url%20%3D%20urls%5B0%5D%0A%09url%2Efetch%28function%28data%29%7B%0A%09%09console%2Elog%28data%2EtoString%28%29%29%0A%09%7D%29%0A%7D%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
omnioutliner://localhost/omnijs-run?script=try%7Bvar%20picker%20%3D%20new%20FilePicker%28%29%0Apicker%2Efolders%20%3D%20false%0Apicker%2Emultiple%20%3D%20false%0Apicker%2Etypes%20%3D%20%5BFileType%2EplainText%5D%0A%2F%2F%20DECLARE%20THE%20PROMISE%20VARIABLE%0Avar%20pickerPromise%0A%2F%2F%20CHECK%20PLATFORM%0Aif%28app%2EplatformName%20%3D%3D%3D%20%22iOS%22%29%7B%0A%09msg%20%3D%20%22Tap%20%E2%80%9DContinue%E2%80%9D%20and%20choose%20a%20text%20file%20from%20the%20forthcoming%20file%20picker%3A%22%0A%09var%20alert%20%3D%20new%20Alert%28%22Select%20File%22%2C%20msg%29%0A%09alert%2EaddOption%28%22Continue%22%29%0A%09alert%2Eshow%28function%28result%29%7B%0A%09%09pickerPromise%20%3D%20picker%2Eshow%28%29%0A%09%7D%29%0A%7D%20else%20if%20%28app%2EplatformName%20%3D%3D%3D%20%22macOS%22%29%7B%0A%09picker%2Emessage%20%3D%20%22Choose%20the%20text%20file%20to%20import%3A%22%0A%09pickerPromise%20%3D%20picker%2Eshow%28%29%0A%7D%0A%2F%2F%20READ%20CONTENTS%20OF%20THE%20CHOSEN%20TEXT%20FILE%0ApickerPromise%2Ethen%28function%28urls%29%7B%0A%09url%20%3D%20urls%5B0%5D%0A%09url%2Efetch%28function%28data%29%7B%0A%09%09console%2Elog%28data%2EtoString%28%29%29%0A%09%7D%29%0A%7D%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
omnifocus://localhost/omnijs-run?script=try%7Bvar%20picker%20%3D%20new%20FilePicker%28%29%0Apicker%2Efolders%20%3D%20false%0Apicker%2Emultiple%20%3D%20false%0Apicker%2Etypes%20%3D%20%5BFileType%2EplainText%5D%0A%2F%2F%20DECLARE%20THE%20PROMISE%20VARIABLE%0Avar%20pickerPromise%0A%2F%2F%20CHECK%20PLATFORM%0Aif%28app%2EplatformName%20%3D%3D%3D%20%22iOS%22%29%7B%0A%09msg%20%3D%20%22Tap%20%E2%80%9DContinue%E2%80%9D%20and%20choose%20a%20text%20file%20from%20the%20forthcoming%20file%20picker%3A%22%0A%09var%20alert%20%3D%20new%20Alert%28%22Select%20File%22%2C%20msg%29%0A%09alert%2EaddOption%28%22Continue%22%29%0A%09alert%2Eshow%28function%28result%29%7B%0A%09%09pickerPromise%20%3D%20picker%2Eshow%28%29%0A%09%7D%29%0A%7D%20else%20if%20%28app%2EplatformName%20%3D%3D%3D%20%22macOS%22%29%7B%0A%09picker%2Emessage%20%3D%20%22Choose%20the%20text%20file%20to%20import%3A%22%0A%09pickerPromise%20%3D%20picker%2Eshow%28%29%0A%7D%0A%2F%2F%20READ%20CONTENTS%20OF%20THE%20CHOSEN%20TEXT%20FILE%0ApickerPromise%2Ethen%28function%28urls%29%7B%0A%09url%20%3D%20urls%5B0%5D%0A%09url%2Efetch%28function%28data%29%7B%0A%09%09console%2Elog%28data%2EtoString%28%29%29%0A%09%7D%29%0A%7D%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
omniplan://localhost/omnijs-run?script=try%7Bvar%20picker%20%3D%20new%20FilePicker%28%29%0Apicker%2Efolders%20%3D%20false%0Apicker%2Emultiple%20%3D%20false%0Apicker%2Etypes%20%3D%20%5BFileType%2EplainText%5D%0A%2F%2F%20DECLARE%20THE%20PROMISE%20VARIABLE%0Avar%20pickerPromise%0A%2F%2F%20CHECK%20PLATFORM%0Aif%28app%2EplatformName%20%3D%3D%3D%20%22iOS%22%29%7B%0A%09msg%20%3D%20%22Tap%20%E2%80%9DContinue%E2%80%9D%20and%20choose%20a%20text%20file%20from%20the%20forthcoming%20file%20picker%3A%22%0A%09var%20alert%20%3D%20new%20Alert%28%22Select%20File%22%2C%20msg%29%0A%09alert%2EaddOption%28%22Continue%22%29%0A%09alert%2Eshow%28function%28result%29%7B%0A%09%09pickerPromise%20%3D%20picker%2Eshow%28%29%0A%09%7D%29%0A%7D%20else%20if%20%28app%2EplatformName%20%3D%3D%3D%20%22macOS%22%29%7B%0A%09picker%2Emessage%20%3D%20%22Choose%20the%20text%20file%20to%20import%3A%22%0A%09pickerPromise%20%3D%20picker%2Eshow%28%29%0A%7D%0A%2F%2F%20READ%20CONTENTS%20OF%20THE%20CHOSEN%20TEXT%20FILE%0ApickerPromise%2Ethen%28function%28urls%29%7B%0A%09url%20%3D%20urls%5B0%5D%0A%09url%2Efetch%28function%28data%29%7B%0A%09%09console%2Elog%28data%2EtoString%28%29%29%0A%09%7D%29%0A%7D%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
(async () => { try { picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] if(app.platformName === "iOS"){ msg = "Tap ”Continue” and choose a text file from the forthcoming file picker:" alert = new Alert("Select File", msg) alert.addOption("Continue") await alert.show() } else if (app.platformName === "macOS"){ picker.message = "Choose the text file to import:" } urls = await picker.show() urls[0].fetch(function(data){ console.log(data.toString()) }) } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } })();
Prompting the User
  

(async () => { try { picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] if(app.platformName === "iOS"){ msg = "Tap ”Continue” and choose a text file from the forthcoming file picker:" alert = new Alert("Select File", msg) alert.addOption("Continue") await alert.show() } else if (app.platformName === "macOS"){ picker.message = "Choose the text file to import:" } urls = await picker.show() urls[0].fetch(function(data){ console.log(data.toString()) }) } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } })();
ios-filepicker-prompt

JavaScript Promises and the FilePicker

A JavaScript Promise is standard built-in object of the JavaScript language. The JavaScript Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.

The use of JavaScript Promises with Omni Automation, and the FilePicker class in particular, enables a simpler design for your scripts and actions that are meant to respond to events, such as the presentation and completion of a file picker dialog.

Instead of writing complex functions containing multiple call-backs, using JavaScript promises, scripts can divided into functions that are individually called when file picker dialogs have been either approved or dismissed.

Promises are generated when a file picker is displayed using the show(…) method of the FilePicker class. The resulting promise is stored in a variable so that its functions for responding to the dialog approval or cancelation can be identified and isolated in the script.

Run the following example script and view the scripting console after closing the file picker dialog to see the results of the promise.

FilePicker with Explicit Promise


// FILEPICKER INSTANCE CREATED AND PROPERTIES SET picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] // PICKER PROMISE IS CREATED AND STORED WHEN PICKER IS DISPLAYED pickerPromise = picker.show() // PROMISE FUNCTION CALLED UPON PICKER APPROVAL pickerPromise.then(urlsArray => { aURL = urlsArray[0] // processing statements using url(s) console.log(aURL) }) // PROMISE FUNCTION CALLED UPON PICKER CANCELLATION pickerPromise.catch(err => { console.log("form cancelled", err.message) })

 02  Create a new empty file picker object and store it in a variable.

 03-05  Set the properties of the new file picker.

 08  Use the show(…) method of the FilePicker class to display the file picker dialog to the user. The result of this method is a new JavaScript Promise object that is stored in a variable.

 11-15  The then(…) method of the Promise class is used to process the promise upon approval of the file picker dialog. An array of URLs for the chosen items is passed into the method’s call-back function.

 18-20  A function that is called if the file picker dialog is cancelled.

Inline Promise Processing

As shown in the example above, the result of using the show(…) method to display a file picker dialog is a JavaScript promise object, which can then be processed using the then(…) or catch(…) functions depending on whether the dialog was approved or canceled.

If processing a canceled picker is not necessary for your script, you can streamline the script coding by appending the then(…) function to the show(…) function as shown in the example below. Functionally, this technique is the same as processing a promise object stored in a variable. The callback function within the then(…) function still receives the array of URLs chosen by the user in the file picker dialog.

Pick and Process


picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] picker.show().then(function(urlsArray){ aURL = urlsArray[0] aURL.fetch(function(data){ console.log(data.toString()) }) })

 01  Create a new empty file picker object and store it in a variable.

 02-04  Set the properties of the new file picker.

 05  Use the show(…) method of the FilePicker class to display the file picker dialog to the user. The result of this method is a new JavaScript Promise object that is then processed by the appended then(…) function.

 06  A URL object is extracted from the URLs array passed by the promise as input to the call-back function.

 07-09  The fetch(…) method of the URL class is used to read the file represented by the extracted URL.

Asynchronous Promise Processing

The example uses await statements within an asynchronous wrapper rather than explicit Promise blocks for implementing the file picker and the retrieval of file data.

Note that the FilePicker code is placed within an asynchronous (async) function wrapper, and the call to show the FilePicker is preceded by the term: await

The resulting array of URLs will be passed to the declared variable: urlsArray

Asynchronous FilePicker


(async () => { try { picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] urlsArray = await picker.show() urlsArray[0].fetch(function(data){ console.log(data.toString()) }) } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } })();

Promise Documentation

JavaScript Promises offer a high degree of customization beyond the basic use with forms shown here. Detailed information regarding JavaScript Promises is available at:

Using URL.FetchRequest to Read Chosen File

The example uses await statements within an asynchronous wrapper rather than explicit Promise blocks for implementing the file picker and the retrieval of file data.

The script uses the fetch() function with the URL.FetchRequest class to asynchronously read the contents of a file, rather than using the fetch() function of the URL class:

Read Contents of Text File


(async () => { try { picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] urls = await picker.show() fileRequest = new URL.FetchRequest() fileRequest.url = urls[0] fileContents = await fileRequest.fetch().then(file => file.bodyString) Pasteboard.general.string = fileContents } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } })();