(▼)(▲)
This space is currently unused, and I'm not quite sure what to do with it. If you have any ideas, I would love to hear them.

Find me on Mastodon

Recursive Actions In Launch Center Pro

(▲)

March 17, 2014

Yesterday, I was playing around with some deep nested actions and multiple levels of encoding in Launch Center Pro. The results of that were largely useless, but they did give me an idea for how to get recursive actions working in LCP, and the results of that are described in detail below. Be forewarned: this gets pretty technical. If you don't want to bother with the inner workings of the method, you can jump to the bottom and get your hands on the recursive Fantastical action via the direct import link (▼)(▲)Launch the action in LCP to be presented with a prompt box for an event that you want to be parsed in Fantastical and added to your calendar. Tap Launch and Fantastical will automatically parse and add your event, then take you back to Launch Center Pro where you are automatically presented with another prompt box for the same thing again. The loop goes on until you hit cancel on a prompt box. If you want to add multiple events to your calendar, this is the fastest, easiest way to do so..

Unlike Drafts, Launch Center Pro does not allow you to save actions by a certain name and then call that name to run the action. This would seem to render recursive actions impossible in Launch Center Pro, as we only create them in Drafts by having an action call its own name and run itself again when it completes. We can have an action cycle a handful of times by manually nesting it in its own x-success variable, but this method means that the action will only run a preset amount of times (the number of times you nest it inside itself, no more and no less), and that does not make it very versatile. Furthermore, every level of nesting requires another level of encoding, and that gets extraordinarily complex very quickly. What we want isn't a massive, incomprehensible chain, it's a small chain, with no more than a single x-success, that will loop infinitely until we cancel it. Since we can't save an action by a name and then call it, there's only one other way: variables. Unfortunately, LCP only supports a single persistent variable (▼)(▲)A persistent variable is one that is maintained with the same value across multiple actions. In Drafts, you can fill in a variable in the first action, then call that variable in the second action and get the same value., the [clipboard] variable.

In general I dislike clipboard hacks, as they clear whatever is on your clipboard and also render the clipboard variable itself useless in the actions. In this case, however, it's the only way. In order to get a recursive action working in LCP, we first have to load the action onto the clipboard, then place the contents of the clipboard in the URL action. Launch Center Pro has a parameter in its URL scheme called “url”, and if we put an action in that parameter, LCP will call itself and then call the URL. That basic action looks like this:

launchpro://?url=[URL To Launch]

Note that the URL you are launching needs to be encoded.

From here the process gets more complex. At first I thought I would need two actions, one to prep the clipboard and another to run it. Then I realized that we can do it all in one single action, but it requires quite a bit of encoding. First we have to copy the action we are repeating to the clipboard. Since LCP's built in clipboard actions support x-callback-url, we can chain another Launch Center Pro action to the x-success variable of the clipboard action, then call the contents of the clipboard in the url parameter of the action. In case this doesn't sound complex enough already, there are still a few more issues to consider as well (bear with me, it's worth it in the end).

The way the LCP URL handler works is to first look at the top level of the URL (the unencoded part) and fill in any variables there (that would be calling [prompt]s and [list]s and swapping the user's input for the variable in the code, as well as also replacing any [clipboard] variables for the contents of the clipboard). This means that if it finds any unencoded, bracketed variables, it will fill them in before the action runs. Since we want to place our action in the clipboard before we put its contents in the URL, the clipboard variable has to be encoded for the first part of the action, which means placing it in the URL parameter of a Launch Center Pro action. Unfortunately, placing it one level deep isn't quite enough. Due to how the URL handler works, the URL action on the clipboard has to be placed in the action before LCP parses the level of the code that the clipboard is on. I know that's a confusing statement, so I'll try to clear it up below. Here's the action:

launchpro://?url=launchpro%3A%2F%2F%3Furl%3Dlaunchpro%253A
%252F%252F%253Furl%253D%5Bclipboard%5D

And here's the breakdown of that action:

First, LCP reads the unencoded portion of the URL, launchpro://?url=. All this does is call itself and step forward, unencoding the next level of the URL action. That may seem frivolous, but it's necessary when we are placing the action in the x-success variable of another action because we want to move a level away to avoid prematurely filling in the clipboard variable. Now the action, decoded by one level and with the first portion thrown away, looks like this:

launchpro://?url=launchpro%3A%2F%2F%3Furl%3D[clipboard]

At this point LCP sees a variable, [clipboard], and swaps it out for the contents of the clipboard. Since we are chaining this to a clipboard action that loads a URL action onto our clipboard, that will be what is placed here. Let's say the URL action we are loading simply launches Drafts, and thus would look like this: drafts://. Note that the part of the URL that the clipboard is being exchanged for is in an otherwise encoded region of our action. This is because LCP will not run a URL action from the clipboard if we fill it in in the same step that we are running it (I'm not actually sure why that doesn't work). Since we're placing it in an encoded spot, the action itself needs to be encoded too. However, it's also coming after a ?url=, so it has to be encoded for that as well. The result is that we double encode whatever is going in the clipboard. In this example, LCP adds drafts%253A%252F%252F (drafts:// double encoded) into the action from the clipboard, then calls itself, steps forward, and decodes and runs the next portion of the URL. Since the clipboard was just filled in, it looks like this:

launchpro://?url=drafts%3A%2F%2F

Finally, LCP calls itself once more, steps forward, decodes, and completes by running the simple drafts:// and launching Drafts.

So that's how we run an action which we have saved in the clipboard, but this is part of a bigger subject, so remember that all of that is happening in the x-success variable of an LCP clipboard action. The clipboard action is what is loading the hard-coded URL action onto the clipboard and then launching it later, and the entire thing (minus the action we're loading) looks like this:

launch://x-callback-url/clipboard?text={{[Your Encoded Action 
Here]}}&x-success={{launchpro://?url=launchpro%3A%2F%2F%3Furl
%3Dlaunchpro%253A%252F%252F%253Furl%253D%5Bclipboard%5D}}

So now we understand how to hard code an action into the clipboard and then call that action later on in an x-success, but how does this allow for recursive actions? The key is that the clipboard is the only value in LCP's URL scheme which never changes. The first step is that your chosen action needs to support x-callback-url. Once you've picked an action, you simply use the same method that we used above for the x-success value, calling Launch Center Pro and running [clipboard] from the URL parameter. Let's use a Fantastical URL action as an example:

fantastical2://x-callback-url/parse?sentence=Test&add=1
&x-success=launchpro%3A%2F%2F%3Furl%3Dlaunchpro%253A
%252F%252F%253Furl%253Dlaunchpro%25253A%25252F%25252F%25253F
url%25253D%255Bclipboard%255D

This action launches Fantastical 2, sends it the word "Test" to be parsed, automatically adds the event, then calls x-success and decodes and launches the exact same LCP action that we broke down earlier:

launchpro://?url=launchpro%3A%2F%2F%3Furl%3Dlaunchpro%253A
%252F%252F%253Furl%253D%5Bclipboard%5D.

To finish, we encode our entire Fantastical URL and place it in the text parameter of the first LCP action, which saves it to the clipboard so it can be repeated (recall that the action needs to be encoded twice, but we can do one of these automatically within the code by wrapping the action, single encoded, in {{curly brackets}}. You still need to manually encode it once on your own though). Run the action in LCP and you'll get an infinite loop adding "Test" events to Fantastical (If you do run it, remember you can just press the home button to cut the loop). Obviously, that isn't very useful. What would be useful would be to have LCP throw up a prompt box each time so that you can add actual events. This is easy, just replace “Test” with [prompt] (although make sure it's encoded with the rest of the action as “%5Bprompt%5D”). Now if you run the action, LCP will prompt you for an input each time, meaning you can automate almost the entire process of adding multiple events to Fantastical directly from Launch Center Pro. The only difference between this version and the Drafts version is that in Drafts you write up the whole list and then call the action on everything at once, whereas in LCP you write one event at a time and then wait about one second for it to go add the event before jumping back to get the text for another. When you're finished adding, just hit cancel on the next prompt box and the process comes to an end.

Complete with the [prompt] input variable, the final product will look like this:

launch://x-callback-url/clipboard?text={{fantastical2%3A%2F
%2Fx-callback-url%2Fparse%3Fsentence%3D%5Bprompt%5D%26add%3D1%26
x-success%3Dlaunchpro%253A%252F%252F%253Furl%253Dlaunchpro%25253A
%25252F%25252F%25253Furl%25253Dlaunchpro%2525253A%2525252F
%2525252F%2525253Furl%2525253D%25255Bclipboard%25255D}}
&x-success={{launchpro://?url=launchpro%3A%2F%2F%3Furl%3Dlaunchpro
%253A%252F%252F%253Furl%253D%5Bclipboard%5D}}

That code block probably looks pretty daunting, but the best part about this method is that it's almost identical for any action you want to create. You can reuse that exact URL action, but simply change the Fantastical part, and get an entirely different action which works the same way. Just use this format, but paste in your own action (make sure it is encoded a single time!) in the designated area:

launch://x-callback-url/clipboard?text={{[Your Encoded Action 
Here]}}&x-success={{launchpro://?url=launchpro%3A%2F%2F%3Furl
%3Dlaunchpro%253A%252F%252F%253Furl%253D%5Bclipboard%5D}}

Inside of your action, make sure the x-callback-url parameter looks exactly like this:

launchpro%3A%2F%2F%3Furl%3Dlaunchpro%253A%252F%252F
%253Furl%253Dlaunchpro%25253A%25252F%25252F%25253F
url%25253D%255Bclipboard%255D

(That is only single encoded, so you can place it directly on the end of the &x-success= value of whatever action you use before you encode the action. Don't forget that you will then need to encode the entire action before you paste it into the code template, so this part after your x-success will actually end up double encoded.)

The recursive Fantastical action is the most useful one I've come up with so far, but make sure to ping me if you come up with any other cool uses for this method of recursion.

You can grab the fully functional recursive Fantastical action we built above through this Launch Center Pro direct import link.