Implementation Details for OmniFocus 2.14 Automation

Automation in OmniFocus 2.14

OmniFocus 2.14 improves automation by adding support for two-way communication with other iOS apps.

In iOS, the primary way that apps talk to each other is through URLs. These are much like the URLs you enter into a web browser (in fact, you can enter these exact URLs into a web browser if you wish) and provide a mechanism for an app to transmit information to another app.

OmniFocus included support for automation through URLs on the day the App Store first launched, back in 2008. Back then, URLs were considered one-way mechanisms: one app could send something to another app, but that was it: the original iPhone would only run one app at a time, so the starting app was gone and would never hear anything back. But even that much was quite useful: for example, you could add a Safari bookmark which would send the current web page to OmniFocus. And many apps have taken advantage of this primitive mechanism from 2008 to send a new task to OmniFocus or to ask OmniFocus to show a particular item or perspective.

Well, how time flies! Fast forward eight years. Since 2008, the device hardware and the iOS operating system have both evolved significantly, and on most devices itā€™s now possible to have multiple apps running at the same time (one in the background)ā€”and on some devices, you can even have two apps simultaneously running side-by-side.

And over those eight years, the app ecosystem has evolved as well. Many appsā€”like Workflow, Editorial, Drafts, 1Writer, and Ulyssesā€”now support two-way communication by through the use of ā€œcallbackā€ URLs, as proposed at x-callback-url.com. Using those apps you can build automation workflows that no longer stop the moment you transfer control to another app: when that app finishes its work, it uses a different URL to ā€œcall backā€ to the next step in the workflow.

With OmniFocus 2.14, Iā€™m very pleased to be able to say that OmniFocus now includes best-of-class support for callback URLs. At its simplest, this means that you can create a workflow that adds more than one item to OmniFocus. But we didnā€™t just add support for two-way communication between OmniFocus and other apps, we added support for doing for automating a whole lot more of the powerful capabilities of OmniFocus.

This means, for example, that you can now create a text file outlining an entire project, with placeholders for specific names, defer dates, due dates, and so on. You can then run a workflow that processes those placeholders and builds a new project (such as ā€œRun a marathon on June 30ā€) from that project template, and these project templates and their processing workflows can be shared with others.

But thatā€™s just one possible workflow which is enabled by automation. You can also use the time estimates and dates already entered into OmniFocus to schedule tasks on a calendar. Or you could build a workflow which publishes the status of all the tasks in a project. Weā€™ve just barely begun to explore the possibilities!

So thatā€™s what this release is about.


Implementation Details

What follows is a description of the app-to-app interface that can now be used by other apps when talking to OmniFocus. The target audience for this is app developers, who need to see exactly what to do to get their app talking to OmniFocus.

URL Actions

OmniFocus URLs take the form omnifocus:///action?parameter=value&ā€¦. Parameter values should be URL-escaped. For two-way communication between apps, OmniFocus uses the protocol described by x-callback-url.com. The quick summary is that each command accepts optional query parameters x-success, x-cancel, and x-error; their values indicate how OmniFocus should continue the current automation workflow.

Here are the supported URL actions:

Data Entry

  • /add?name=name&ā€¦ - creates a single task

    • name=string - the name of the new task
    • note=string - its note
    • estimate=time span - time estimate, e.g. 2h for 2 hours or 3w for 3 weeks.
    • flag=bool - whether the item is flagged (true or false)
    • parallel=bool - whether children are parallel (true) or sequential (false)
    • autocomplete=bool - whether the item automatically completes itself when its children are complete
    • defer=date - defer until date, e.g. 2016-04-19 5pm or next Thursday -3d
    • due=date - due on date
    • completed=date - completed on date
    • repeat-method=method - the repeat method: fixed, start-after-completion, or due-after-completion
    • repeat-rule=rule - an ICS repeat rule (see RFC2445), e.g. FREQ=WEEKLY;INTERVAL=1
    • project=string - the project to assign
    • context=string - the context to assign
    • attachment-name=string - attachment file name, repeated as necessary for multiple attachments
    • attachment=attachment-data - base-64 encoded file content, repeated as necessary
    • reveal-new-item=bool - whether to navigate to the newly created item (true or false)

    Callback: on success, the result query parameter will contain a URL that can be used to navigate to that task (e.g. omnifocus:///task/abc123), while the parent query parameter will contain the URL for the assigned parent project (if any).

  • /paste - pastes a hierarchy of items into the Inbox or project list

    • target=target - use inbox to create new inbox items, or projects to create top-level projects
    • index=number - where the new item should appear in the list: 1 (top) to n or -1 (bottom) to -n
    • content=TaskPaper string - the content to paste, in TaskPaper format (if unspecified, will read text from the pasteboard)

    Callback: on success, the result query parameter will contain a URL that can be used to navigate to that task (e.g. omnifocus:///task/abc123).

Navigation

  • /home - navigates to the top level of the app
  • /inbox - navigates to Inbox
  • /projects - navigates to Projects
  • /contexts - navigates to Contexts
  • /nearby - navigates to Nearby
  • /forecast - navigates to Forecast
  • /flagged - navigates to Flagged
  • /review - navigates to Review
  • /search?q=string - opens the search view
  • /context/context-id - navigates to a context
  • /folder/folder-id - navigates to a folder
  • /perspective/perspective-id - navigates to a custom perspective
  • /task/task-id - navigates to a project or action

General

  • /app-info - returns information about the current OmniFocus version
  • /parse-date?input=string - parses a date (e.g. next thu 5pm, or 7/1 -90d) based on current locale settings

Sharing

While viewing a task or project, you can use the Share button to send that task to another app.

Several representations of the task are included on the pasteboard:

  • TaskPaper text - TaskPaper text is the most human-readable representation of a task, suitable for copying into any text editor. See below for details.

  • Navigation URL - A simple navigation URL will be placed on the pasteboard.

  • Reference URL - A long navigation URL with a fully populated set of parameters (just like the /add URL action). This URL does not include attachments at the moment; if those attachments would be useful to you, please let us know!

TaskPaper Format

TaskPaper text is a simple but flexible format for representing tasks as text. It can be edited with any text editor. Quoting from the TaskPaper Users Guide:

TaskPaperā€™s file format is fairly simple. Hereā€™s how TaskPaper reads a file:

  • Files are expected to use the UTF-8 encoding and use ā€˜\nā€™ to separate lines.

  • A task is a line that begins with a hyphen followed by a space ('- ') which can optionally be prefixed (i.e indented) with tabs or spaces. A task can have zero or more tags anywhere on the line (not just trailing at the end).

  • A project is a line that isnā€™t a task and ends with a colon (ā€˜:ā€™), or a colon (ā€˜:\nā€™) followed by a newline. Tags can exist after the colon, but if any non-tag text is present, then it wonā€™t be recognized as a project.

  • A note is any line that doesnā€™t match the task or project rules.

  • Indentation level (with tabs, not spaces) defines ownership. For instance, if you indent one task under another task, then it is considered a subtask. Tasks and notes own all objects that are indented underneath them. Empty lines are ignored when calculating ownership.

  • A tag has the form ā€œ@tagā€, i.e. it starts with an ā€œatā€ character (ā€œ@ā€), followed by a run of non-whitespace characters. A tag can optionally have a value assigned to it. The value syntax immediately follows the tag word (no whitespace between) and is enclosed by parentheses: ā€˜(ā€™ and ā€˜)ā€™. The value text inside can have whitespace, but no newlines. Here is an example of a tag with a value: @tag(tagā€™s value)

As you can see, one issue with this format is that TaskPaper doesnā€™t have any mechanism for escaping arbitrary content, so this representation can be lossy. For example, a task name that includes the text @flagged will see that text disappear from its name and the task will end up being flagged. And you canā€™t begin a note with a -. (To prevent mishap, on export we substitute any leading - characters in notes with en-dash ā€“ characters.)

In general, OmniFocus tries to use the same names for tags that it uses for parameters in the /add URL action, with a few modifications (like @done) to match the way other apps interpret TaskPaper content.

OmniFocus currently supports the following tags:

  • @estimate(time span) - time estimate, e.g. 2h for 2 hours or 3w for 3 weeks.
  • @flagged - present when an item is flagged
  • @parallel(bool) - whether children are parallel (true) or sequential (false)
  • @autodone(bool) - whether the item automatically completes itself when its children are complete (named to match @done)
  • @defer(date) - defer until date, e.g. 2016-04-19 5pm or next Thursday -3d
  • @due(date) - due on date
  • @done(date) - completed on date
  • @repeat-method(method) - the repeat method: fixed, start-after-completion, or due-after-completion
  • @repeat-rule(rule) - an ICS repeat rule (see RFC2445), e.g. FREQ=WEEKLY;INTERVAL=1
  • @context(string) - the context to assign

If youā€™ve read this far, I hope youā€™ve gained a clearer understanding of how all of this works. If anything was confusing or left you with unanswered questions, please donā€™t hesitate to contact us!

16 Likes

Very useful, thanks!

How about importing project templates to a folder within OmniFocus like so:

- New Project @folder(Personal)
    - Task 01
    - Task 02
    - etc.

The missing piece ! Thanks for that.

Why 2 words for the same function (if I understand well) :

  • autocomplete
  • autodone ?

Regarding this functionality, I could not find a way to mark a project or group action to autocomplete directly within the app. Is it only available through the url scheme ?

The plan for the future is that the /paste URL action will accept folder names as targets, not just the ā€œinboxā€ and ā€œprojectsā€ options it currently accepts.

Iā€™m not wholly convinced that I made the right choice here, but since TaskPaper documents call completed items ā€œ@doneā€, for internal consistency (within the format) I went with ā€œ@autodoneā€ to match it. (So in TaskPaper we have ā€œ@doneā€ and ā€œ@autodoneā€, while in the URL action we have ā€œcompleteā€ and ā€œautocompleteā€.)

The autocomplete setting has always been available on Mac, but this is the first time youā€™ve been able to choose that setting on iOS.

1 Like

Ah, excellent.

I remember that there were some bugs (format misapprehensions) in an earlier OmniFocus translator for the TaskPaper format, and I notice that you are using a very old reference document there. Probably worth checking with current documentation:

http://guide.taskpaper.com/getting_started.html

In particular, do be clear that the following is not a project followed by its tasks:

Project:
- task 1
- task 2
- task 3

It is a project followed by three top-level miscellaneous tasks which are its peers and not its children ā€“ nesting (parent child relations) in TaskPaper is only by tabs ā€“ this allows for notes and miscellaneous items which are not in a project. To test this, experiment with folding and filtering operations in a https://www.taskpaper.com/ outline.

A Project containing three tasks would look like this, where the whitespace indent is a tab:

Project:
    - child task 1
    - child task 2
    - child task 3

( I think one of your recent template examples also seemed to be referring to a parent task as a ā€˜projectā€™ )

Remember also that TaskPaper projects nest ā€“ There is no restriction on the depth of sub-projects ā€“ and can therefore be mapped onto folder nests as well as onto projects, tasks and sub-tasks.

1 Like

This sidesteps the problem of specifying folders in the Taskpaper format when trying to add tasks, but as I understand it the problem remains for copying OmniFocus structures as Taskpaper lists. Currently this is not possible for folders - they currently do not even have a share option from which to copy (only status, move, and delete).

If Taskpaper is indeed to become the cannonical Pasteboard format of choice, this would be great to have in some way.

3 posts were split to a new topic: Mapping OmniFocus structures to TaskPaper

That will be very helpful. Along similar lines, are there plans to allow a specific project to be specified as a target?

The new ā€œPasteā€ option is very useful for adding groups of tasks, but sometimes I just want to add several items to a single action list, or a sub-project to an existing project. Current TaskPaper parsing doesnā€™t seem to provide any way to accomplish this, unless Iā€™m missing somethingā€¦

Yes, the plan is to let you specify either. (Perhaps even a task within a project, if you know its reference URL.)

1 Like

This is fantastic to have (Iā€™m using editorial).

The only things I am missing, in order of decreasing importance:

  1. The ability to make a project in a folder.
  2. My taskpaper syncs via dropboxā€¦ something on OSX that can use these templates in the same way that the editorial workflow can in iOS.
  3. Conditional tasks (I want to say ā€˜if variable = x then delete this taskā€™ - this lets me set a variable when running a task that gives very different lists (trivial - if a packing list for a trip is ā€˜abroadā€™ include the ā€˜passportā€™ item, otherwise delete it).
  4. Variable options (select from a list of options, rather than typing in the item).

This is fantastic!

Just wondering what the taskpaper syntax is for assigning a nested context:

Tried these but none worked

- Ā«JiraNoĀ» @parallel(false)
    - Task 1 @context(Work:Coding)
    - Task 2 @context(Work/Coding)
    - Task 3 @context(Work Coding)

Any ideas?

Try @context(Work : Coding). (You can also use Coding if thatā€™s the first Coding context in your context hierarchy.)

One way to figure this sort of thing out using the app itself is to create a project or task laid out the way you want it to be in OmniFocus (e.g., create a task and assign it to Work : Coding), then use the Share button to copy to to your pasteboard.

1 Like

Great - thanks, the spaces either side of the colon did the trick!

I think Iā€™ve found a bugā€¦

I have been making taskpaper tasks with things like this - but when I enter a date like ā€˜next mondayā€™ the due date ends up before the defer - and when I use a date like 1/1/2017 it puts both dates as 31/12/2016

 - Pack for Ā«Trip NameĀ»  @defer(Ā«dateoftripĀ»-8d 9am) @due(Ā«dateoftripĀ»-1d 9am) @autodone(true) @parallel(true)
	- Pack Hand Luggage for Ā«Trip NameĀ» Trip @autodone(true)
		- Plane specific items for Ā«Trip NameĀ» @autodone(true)
			- Thing 1 for Ā«Trip NameĀ» trip
			- Thing 2 Ā«Trip NameĀ»
			- Thing 3 Ā«Trip NameĀ»
		- Pack Documents for Ā«Trip NameĀ» @autodone(true)
			- Airport Car Park Info for Ā«Trip NameĀ»
			- Plane info for Ā«Trip NameĀ»

I have the fix, I need to enter dates as yyyy-mm-dd - not the end of the world, but dd/mm/yyyy would be nice (though youā€™d have to play nice with mm/dd/yyyy - probably take from user preference)

Interestingly, ā€œ1/1/2017ā€ on its own works just fineā€”itā€™s just going awry somewhere when it tries to perform the relative manipulation of that date (ā€œ1/1/2017 -8d 9amā€). Which is strange, since (as you note) the ISO form doesnā€™t have this issue.

We look at the current localeā€™s date format to determine the preferred date order, but no matter what locale youā€™re using we generate and accept dates in the ISO ā€œ2017-01-01ā€ format (since otherwise we can end up in situations where the userā€™s preferred format doesnā€™t actually include information we need, e.g. ā€œ1/1ā€ without a year).

Iā€™ll leave that one in your inbox, then :)

In the meantime, Iā€™ve renamed my variable as Ā«travel yyyy-mm-ddĀ» to give me a reminder when I need it.

I think the new TaskPaper parser has a regex bug.

In TaskPaper I often put the due date first in order to prioritize/sort when viewing a list of tasks due dates:

  • @due(2016-05-01) thing 1
  • @due(2016-06-01) thing 2
    Etc.

When I try to use ā€œomnifocus:///pasteā€ or the Editorial workflow to capture and convert these, the OF task name includes the ā€œ@dueā€ part and the due date is not parsed into the OF task.

Perhaps this isnā€™t blackletter TaskPaper syntax, but I feel that OF should handle this and I imagine that it would be easy to fix.

ā€œsomething on OSX that can use these templates in the same way that the editorial workflow can in iOSā€

interestingly calling an omnifocus:// url on the mac does open OmniFocus , but none of the other actions do anything. Given how robust the AppleScript support in OF for Mac is, it wouldnā€™t be too hard to write a URL handler that takes an OF URL and executes the actions via AppleScript. Except for callbacks. It wouldnā€™t surprise me if this came in an update to OF for Mac eventually.