My Setup and Tech Stack to Streamline Grocery List Management with GTD, OmniFocus, and Siri Automation

For Mac and iOS power users, especially those practicing Getting Things Done, combining OmniFocus, automation, and Shortcuts can streamline and optimize everyday tasks. In this guide, I’ll dive into how I manage my grocery lists using these tools, transforming a mundane task into an efficient, automated process.

Easy Capture with Siri

The key to an effective grocery list management system is capturing items as soon as they come to mind. With Siri, this process becomes effortless.

When I notice we’re running low on something, I simply say to Siri, “Remind me to buy more arugula.” This creates a reminder in the Reminders app. OmniFocus has a built-in setting to automatically scoop up new reminders from the iOS Reminders app. After a few seconds, the “Buy more arugula” reminder is in my OmniFocus Inbox.

OmniFocus Automation for Efficiency

To handle these reminders, I’ve written an Omni automation script that seamlessly integrates with OmniFocus. Here’s how it works:

  1. Scan the OmniFocus Inbox: My script scans for items that begin with the phrase “buy more.”
  2. Move to Grocery Project: It then moves these items into my “Grocery Shopping List” project. This is also the Project where I add ingredients from my other workflow, tailored toward meal preparation.

This step ensures all grocery-related tasks are organized in one place, making it easier to manage and review.

Omni Automation code that I run as an OmniFocus plugin:
(() => {
    const action = new PlugIn.Action(function(selection, sender){

        try {
            const buyMorePhrase = 'buy more';
            const groceryProjectName = 'Grocery Shopping List';
            const groceryProject = flattenedProjects.byName(groceryProjectName);
            var tasks = [];
    
            inbox.forEach(task => {
                if (task.name.toLowerCase().includes(buyMorePhrase)) {
                    tasks.push(task);
                }
            });

            if (groceryProject && tasks.length > 0) {
                moveTasks(tasks, groceryProject.beginning);
                
                // go to project
                const matchedProjects = flattenedProjects.filter(project => project.name === groceryProjectName);

                if (matchedProjects.length === 0) {
                    console.log("Project not found.");
                } else {
                    const projectID = matchedProjects[0].task.id.primaryKey;
                    URL.fromString(`omnifocus:///task/${projectID}`).open();
                }


            }
        }
        catch(err){
            new Alert(err.name, err.message).show();
            console.error(err);
        }
    });

    action.validate = function(selection, sender){
        // validation code
        return true
    };
    
    return action;
})();

Shortcuts for Seamless Integration

To further streamline the process, I created a Shortcut that handles moving appropriately tagged items from OmniFocus back to Reminders.

Here’s how the Shortcut works:

  1. Identify Tagged Items: The Shortcut scans OmniFocus for items in my Grocery Shopping List project that begin with “buy more.”
  2. Move to Reminders: These items are transferred to the Reminders app, and the text “buy more” is removed from the task title.
  3. Cleanup: The actions are then deleted from OmniFocus, keeping my task manager clutter-free.

I recently started working from Reminders at the grocery store because a recent iOS update allows for automatic categorization of groceries—something I used to do manually. So, “buy more arugula” becomes a reminder “Arugula” within the “Produce” category.

This integration between OmniFocus and Reminders leverages the strengths of both apps, providing a dynamic and flexible grocery list management system.

The anatomy of the Shortcut:

  1. Runs an Omni Automation script which to get the items
  2. Loops through all the items
  3. Gets the title, replaces “Buy more” with “” (nothing), capitalizes as title case, because #tidy
  4. Gets the title and ID
  5. Adds to Reminders
  6. Returns the ID into another Omni Automation script which handles deleting the moved items from the original OmniFocus project
Omni Automation code run from inside the Shortcut
(() => {
    // Define the name of the project to search in
    const projectName = "Grocery Shopping List";

    // Define the search phrase
    const searchPhrase = "buy more";

    // Define the excluded words
    const excludedWords = ["automation", "create"];

    // Function to get all tasks from a specific project that contain a specific phrase
    const getTasksWithPhrase = (projectName, searchPhrase, excludedWords) => {
        // Access all projects
        const projects = flattenedProjects;

        // Find the project by name
        const project = projects.byName(projectName);

        // If the project is found
        if (project) {
            // Get all tasks in the project
            const tasks = project.flattenedTasks;

            // Filter tasks
            const filteredTasks = tasks.filter(task => {
                // Check if task is not completed or dropped
                if (task.taskStatus == Task.Status.Completed || task.taskStatus == Task.Status.Dropped) {
                    return false;
                }

                // Check if task contains the search phrase (case insensitive)
                if (!task.name.toLowerCase().includes(searchPhrase.toLowerCase())) {
                    return false;
                }

                // Check if task contains any of the excluded words (case insensitive)
                for (const word of excludedWords) {
                    if (task.name.toLowerCase().includes(word.toLowerCase())) {
                        return false;
                    }
                }

                // If all conditions are met, include the task
                return true;
            });

            // Return the filtered tasks
            return filteredTasks;
        } else {
            return [];
        }
    };

    // Get the tasks containing the phrase
    const tasks = getTasksWithPhrase(projectName, searchPhrase, excludedWords);

    // Format the task name and include notes if present
    const formattedTasks = tasks.map(task => {
        let taskName = task.name;

        // Remove "buy more" from the beginning if present
        if (taskName.toLowerCase().startsWith(searchPhrase.toLowerCase())) {
            taskName = taskName.substring(searchPhrase.length).trim();
        }

        // Create a dictionary for each task with title and note
        return {
            id: task.id.primaryKey,
            title: taskName,
            note: task.note || ""
        };
    });

    // Return an array of dictionaries
    return JSON.stringify(formattedTasks);
})();

Omni Automation script which handles deleting OmniFocus actions after moved to Reminders
(() => {
    // Define the task ID to delete
    const taskIdToDelete = "{{ID as Shortcuts Variable}}";

  // Function to delete a task by its ID
    const deleteTaskById = (taskId) => {
        // Access all tasks
        const tasks = flattenedTasks;

        // Find the task by iterating through all tasks
        const task = tasks.find(task => task.id.primaryKey === taskId);

        // If the task is found, delete it
        if (task) {
            deleteObject(task);
            return `Task with ID ${taskId} has been deleted.`;
        } else {
            return `Task with ID ${taskId} not found.`;
        }
    };

    // Call the function to delete the task
    const result = deleteTaskById(taskIdToDelete);

    // Return the result
    return result;
})();

Shortcuts Automation with Grocery Store Wi-Fi Triggers

I’ve recently added a new sequence to further streamline my process. I created a personal automation in Shortcuts that runs when I connect to any of my frequent grocery stores’ Wi-Fi. It performs the following actions:

  1. Run Omni Automation: It runs my OmniAutomation script to move “buy more” Inbox items to the grocery project.
  2. Execute Shortcut: It then runs my Shortcut to handle renaming and moving grocery items from OmniFocus to Reminders.
  3. Open Lists: Finally, it opens both my OmniFocus Grocery Shopping List and my Reminders Grocery Shopping List.

Future Enhancements for Grocery List Management

Currently, other people in my household can add new grocery items to the Reminders app. Since I handle most of the meal planning, we sometimes end up with duplicate items. It’s not a big issue, but as a data nerd, I’m considering ways to handle this more efficiently.

I’m also thinking about some type of grocery inventory, where I could pull recently completed grocery items from Reminders to build some type of current grocery inventory. Hmm 🤓

What do you think?

Have ideas for enhancing this system? What’s your tech stack for handling groceries? Let me know in the comments!

Leave a Reply

Your email address will not be published. Required fields are marked *