How To Copy Reminders In macOS Catalina With a Script

You can't copy and paste or print Reminders in macOS Catalina. However, with the help of a script you can get a Reminders list as text to be pasted into any app, printed, or sent as email. Learn how to create a Mac script in JavaScript (JXA) using Script Editor or Automator to get this important function back and have it available as an easy-access menu command.
You can also watch this video at YouTube.
Watch more videos about related subjects: Automator (50 videos), Reminders (20 videos).

Video Transcript

Hi this is Gary with MacMost.com. Let me show you how you copy items in a Reminders list in macOS Catalina with the help of a script. 
MacMost is made possible thanks to a great group of supporters. Go to MacMost.com/patreon. There you can read more about the Patreon Campaign. Join us and get exclusive content.
So the problem here is in Catalina you can't copy these items here and paste them into a piece of text. You can't print them. There's no print function. There's no way to get these into another document or to print them out in some way. You can, kind of, use iCloud.com, the web out there to do it. But it's a little tricky and it doesn't work very well. However you can write a script to do it pretty easily.
So let's say we want to get this list and we want to be able to copy it so we can paste it into TextEdit, Pages, Numbers, whatever. I'm going to start with Script Editor. Script Editor is in the Applications folder in the Utilities folder. I'm going to show you at the end how to do this in Automator as well. Now we're going to end up with a pretty long script in Script Editor and if you're not a programmer it can be kind of intimidating. So I want to start off with this simplest version of that script which is just four lines long just to show you how you can do the most basic version of this with just four lines.
So in Script Editor you want to create a new script and you want to make sure you set it to JavaScript.  In JavaScript most of them start off with the same two lines. That's just basically to declare you're going to be using the app call Reminders. Then we're going to do something called includeStandardAdditions and set that to true. That basically says that we're going to be using the app called Reminders and we're going to be scripting it. Now we're going to do most of the work with one line of code.
Start with a variable called text. We're going to put the text into that that we want according to the clipboard. We're going to go with the app Reminders and get the lists from it. So you've got a bunch of different lists like for instance here we've got Reminders, Shopping List, and To Do List. Okay. So we'll get the list. Then we're going to look for the one by name that has this exact name in it. So this simple piece of code has hard coded in it the name of the list. If you wanted to get another list you have to edit the code and change that name.
Then we're going to get the Reminders from that list. So this is just basically the list. This is the reminders inside that list. Then we're going to use a function called name. We know it's a function because it has the little parentheses there. This name function is basically is going to get all the name properties of all those items. It's going to give us that in an array which is basically a list of items inside of JavaScript. So we now end up with this. A list of all of the names of all the reminders in the shopping list. We're then going to use a JavaScript function called Join which takes a list, or array like this, and converts it to text. It's going to put in-between each item a line break. So every item is going to be on its own line. That's going to be in text.
So now that we have that in text we can just use the Set the clipboard to and set it to that text. Let's run it and nothing seems to happen because all it did was put something in the Clipboard. Let's go to TextEdit and I've got a blank document here. I'll Paste in there and there it is! All the items from this Reminders list, the Shopping list. Notice a couple of things. First we don't have any Notes. So here it says bread, whole wheat. That's not there because the name of this item is bread. The note is whole wheat. We only got the names. Another thing is that there are seven items here. But this one has more. Where did they come from? Well, this is only showing us the non-completed items on the list. We had those items in this previously and we checked them off. We completed them. Our script doesn't care about that. We get everything whether it's completed or not completed. So in a lot of different ways this isn't a very good script. It does the most basic things. So let's expand on this and create something very comprehensive.
So here's a complete script that does a lot. I'm going to include the code for this in the post at MacMost.com. So you can just follow along here and then you can copy and paste it later. So we're going to start off exactly the same way. We just say we're using the Reminders app and we want to script it. Now we're going to go and allow the user to choose which list they want. So we're going to use Choose from list. We're going to pass two parameters. The first is the list of items to choose from. So, app.lists.name(). So we're going to get all of the names of the lists. Not the reminders but the lists. So in Reminders here we've got Reminders, Shopping List, and To Do. So it's going to get those three names. It's going to add the Prompt which list? So when I run this, this is what you see. Which List? and it shows you those three there. Cancel out of that and we'll get back to looking at the script.
If the list name then we're going to do all the rest of this stuff. You can see everything else here. It's going to be performed only in the condition that we have a list name. If we don't it's going to skip it all. If you cancel or don't select a list then there's no error message, nothing, it just doesn't do anything. Now we have a list name so let's get the Reminders from that. So we're going to do what we did in the last script. We're going to get from the Reminders app the list. Get the one by name that matches the list name and get all of the reminders. 
Then we're going to get four different aspects of all of those reminders. We're going to get the List Names, which we're going to use .name like we did before. But we're also going to get List Completed. The completed property of each one of those. That's going to be a True or False. So that's .completed. The list of due dates, .duedate and the list of bodies of each reminder. Bodies is the technical term here for the Note that's included with each reminder. So, .body. So now we have four different arrays instead of just one with the names.
Now we're going to create our own JavaScript array. We'll call that List and have that be an empty array. Now we're going to loop through all of the list names and we're going to push onto our new list here an object that looks like this: name colon, completed colon, dueDate colon, and body colon. Each one of those is going to take one element from each of these four lists. So we're going to combine those four lists. We're going to end up with something that looks like this. 
A list where each item is going to look like this. So name: eggs. Completed:false. not completed. dueDate: null, body: null. Then it's going to be milk and bread has an actual body, a note that says whole wheat. If any of them had a due date we'd have a date there rather than null. It's going to be a complete list of all of them. That's what it's going to look like behind the scenes. I'm going to skip this section here and get back to that later. That's just all commented out so it's not doing anything right now.
Now we're going to build some text from that list. So we have an array in JavaScript. That's not what we want. What we want is a piece of text so we can paste it in to TextEdit or whatever. So we're going to create a text piece that's just blank. We're also going to keep track of the number of items in the list so we're just going to call that N and set that to zero for now. Then we're going to loop through all the items in our new list array. Then we're going to get for each item a piece of text. That text is going to start off with the name, so just take whatever the name is, so eggs for instance. Then we're going to look and see if there's a due date. If there is a due date then add to that piece of text a bracket with the word due and a colon and then the dueDate and then a close bracket. Only put that there if there is a due date. If not don't bother to put anything there.
The same thing for Note. We're going to look at the body. If it exists we're going to add to the item something with bracket note colon space and that note and a bracket. Only if there is a note. Otherwise these two lines won't do anything. You'll just end up with just name like eggs. Then we're going to go and take that item and see if it's completed. If it is not completed, not represented by this little exclamation point there, then we're going to add it to the text variable there. I'm going to put a little box before it. I just use the character chooser, you know Control Command Space and search for an empty box there with no checkmark in it, then a space and then that whole piece of text that we generated here and then a new line.
We're going to do the same thing if it's a completed item. So this is not completed and this is completed item. Else The only difference here is we're going to do a checkmark in there. Then we've got in both cases n++ so add one to n. So every time it puts a line in there it's going to add 1. So if it does it for seven items we'll have the number 7 in there.
Now we're going to use that exact piece of code from our short script before to put all of that text into the Clipboard. Then I'm going to display an alert. The reason I'm going to do that because this could take a few seconds to run, especially if the list is long. So we're going to display, basically, an I'm Done message. We're going to use that n there in the message to say seven items copied to the clipboard. Just so you have some information. Let's run this and see what happens. I'll run. I'll select the shopping list. I'll hit OK. It says eleven copied to the clipboard. I'll hit OK there. I'll go to TextEdit. Let's delete this and I'll paste in the new result. You can see all the results there. I have checkmarks next to the completed items so I know what's going on there. I have notes for three of them.
Let's go ahead and make a little modification here. Let's say I don't want to see complete items. All you need to do is comment out these two lines. Now when I run it and select shopping list it's going to seven items copied to the clipboard. When I look and paste those in you'll see this doesn't include the completed items.
Let's try it again but for this To Do list here. It's going to say three items copied to the clipboard. The To Do list actually has three items all with due dates and one with a note. So now we go to TextEdit. I'll paste these in and you'll see all three items there. Each one has a due date and this one has a due date and also a note.
One last thing I want to show you is that in this script we had some sorting options here. So I commented these lines out. If you are using Manual Sort here in Reminders, like I always do, then you're going to get that same manual sort. But if you sort say by Title or Due Date you're still going to get manual sort here unless you say, oh no sort it by something. So I can uncomment this line and now it's going to sort the list using the names. If I, instead, were to uncomment this line it's going to sort by due dates. I can change the direction by changing this here from greater than to less than. So there's a sorting option there. I'm going to leave them comment so that's the way I want them. If you wanted to go a further step you could start working with those.
What if you wanted to put this in Automator. I'll show you why you may want to do that in a second. I'm going to select All and Copy. Then I'm going to run Automator. Create a new document in Automator. Let's say we want to do a Quick Action. Then I'm going to say the workflow receives no input but it only runs in Reminders. So I'll go in the Application and I'll go to Reminders. Now I'm going to search for Run JavaScript here. I'm going to add that. See where it says your script here. I'm just going to paste all of that code in there. Just as is. Then I'm going to Save it. I'll call it Copy Reminders and Save.
So nowit should work the same except that it's inside Automator so it was very easy for us to put it in there as a Quick Action. So let's go to the Reminders app here. I'll then go to Reminders Services and I'll see Copy Reminders. I could run that and then it's going to ask for which list. Let's do the Shopping List again. It's going to say seven copied to the Clipboard. I can go to TextEdit and paste those in. So now I have a convenient way to access this script instead of having it to be file somewhere and having to open it up in Script Editor to run it.
Hopefully eventually you'll be able to copy directly from Reminders but until then this script will do the trick.

Here is the script:

// set things up
var app = Application('Reminders');
app.includeStandardAdditions = true;

// choose list
var listName = app.chooseFromList(app.lists.name(), { withPrompt: "Which List?" });
if (listName) {

	// get the data from the list
	var remindersList = app.lists.byName(listName).reminders;
	var listNames = remindersList.name();
	var listCompleted = remindersList.completed();
	var listDueDates = remindersList.dueDate();
	var listBodies = remindersList.body();
	
	// create a single array
	var list = [];
	for(var i=0; i<listNames.length; i++) {
		list.push({name: listNames[i], completed: listCompleted[i], dueDate: listDueDates[i], body: listBodies[i]});
	}
	
	// sort the list
	//list.sort((a, b) => (a.name > b.name));
	//list.sort((a, b) => (a.dueDate > b.dueDate));

	// build text from list
	var text = "";
	var n = 0;
	for(var i=0; i<list.length; i++) {
	
		// get item item including due date and notes
		var item = list[i].name;
		if (list[i].dueDate) item += " [Due: " + list[i].dueDate + "]";
		if (list[i].body) item += " [Note: " + list[i].body + "]";
		
		// non-completed items
		if (!list[i].completed) {
    		text += "☐ " + item + "\n";
			n++;
			
		// completed items
		} else {
			//text += "☑ " + item + "\n";
  			//n++;
		}
	}
	
	// pass to clipboard
	app.setTheClipboardTo(text);

	// show message
	app.displayAlert("Reminders Copied", { message: n+" copied to the clipboard." });
}

Comments: 12 Comments

    Dana Stevens
    6 years ago

    Extremely interesting. I really enjoy posts like this. I do wish you preferred AppleScript but JavaScript is better than nothing. Thanks for sharing this.

    6 years ago

    Dana: Thanks. As a programmer I find AppleScript to be very ... well, I don't like it at all. JavaScript is not only more modern, but used for a lot of things throughout tech.

    Drew Nelles
    6 years ago

    Dear Gary, Great tutorial, as always. One question: What is the best resource to learn Javascript **for MacOS**? I've looked, but all I find are references to learning Javascript for HTML, and none with Apple specific commands, such as - for example - Is it app.setTextEditto(text); if I wanted to save it to a Text Edit file instead of the clip board above? If there isn't one, what's the best way to make the fork to MacOS from HTML focused Javascript? I'm just beginning to learn JS. Thanks!

    6 years ago

    Drew: There is no "good" resource at all. You can find a scattering of examples online if you search for things like "Mac javascript jxa". For reference, look in the Library in the Script Editor app on your Mac. There is both an AppleScript and JavaScript reference there. It is devoid of examples, but at least you can find the functions and properties there. Only rely on browser JavaScript resources for very basic things like loops, conditions, string functions, etc. And it takes lots of trial and error and Google searching. It IS frustrating.

    John Bianca
    6 years ago

    Nice! Here's what I did. Lol I put my "Grocery" and "To Do" Lists into the NOTES app into one Note. No annoying extra suggestions from Reminders. I can set the size of the text, easy to read, easy to edit, no unwanted Notes, Add Date, or Add Location. And I can copy, paste, print, whatever. Easy peasy. I figure Apple will add the Copy and Print feature to Reminders, but I may stay with NOTES anyway. It's clean, simple, easy to enter, organize, and manage. That's all I want in a LIST app.

    Faisal Jaffer
    6 years ago

    Hi, this was unbeleivably helpful.
    I tried editing the code to list by priority but could not get it to work.
    If its easily possible for you, could you post the script for that.
    Thanks

    Jay Muir
    6 years ago

    Thank you!! This is so helpful and simple to follow. I've been searching for months for a workaround to print Reminders after Catalina blocked the ability to copy/paste. This does the trick perfectly.

    T Falls
    6 years ago

    Excellent! Works perfectly for a standard simple list.

    However, Reminders has 2 new features that are missed with this script; Groups and Sub Tasks. Hoping you or someone posts an update to this script. I have a number of lists that make use of these 2 great features. Thanks!!

    steve
    6 years ago

    Echoing T Falls — I'm stumped on how to get Groups to work in scripting. Any examples would be much appreciated!!

    Christopher Larkin
    6 years ago

    This is very cool - thank you for sharing it! I've been (re)learning JavaScript again because it's so useful with our Google automation.

    Any thoughts on how to get the subtasks in Catalina? I built a few templates in a second list and I'd love to be able to copy items from the template into a new task (with the subtasks) in my main Reminders app.

    6 years ago

    Christopher: I've never tried to work with Reminders subtasks. Since they are very new, there may not be any hooks yet.

    Kenneth Goodman
    6 years ago

    Thanks! Freaking Apple

Comments are closed for this post.