Syncing Delegated Free/Busy Google Calendars to iOS and iPadOS

This post explains how you can sync Free/Busy events from delegated Google calendars to the Apple Calendar app in iOS and iPadOS.

The Problem

I use Apple’s Calendar app to view and manage my calendars in iCloud and Google. Generally, this works well, and all of my directly connected accounts sync fine across the Calendar on macOS, iOS, and iPadOS. There’s one set of calendars, however, that do not sync to all devices, and that’s delegated availability-only (Free/Busy) calendars from Google. This can be problematic if your work calendars can only be shared with Free/Busy information, and you are not participating in a Bring Your Own Device (BYOD) program with your company.

On macOS, this is not an issue, as you can share your work calendar with a personal Gmail account and see the busy events in the Delegates section in the Calendar app.

Delegated Free/Busy calendars appear in macOS

On iOS and iPadOS, however, the delegated calendar is nowhere to be found. This is because iOS and iPadOS do not support delegated Free/Busy calendars from Google. If you go to the Google Calendar Sync Settings page, you’ll see the delegated calendar under the “Not available for syncing” section.

No native delegate Free/Busy calendar support in iOS or iPadOS.
The delegated calendar is not available for syncing from the Google Sync Settings page.

The Solution

So, how do you get Calendar on your iPhone and iPad to see the Free/Busy calendar from work? I searched the internet far and wide for a solution to this problem, but came up with nothing. So I rolled up my sleeves and hacked together this AppleScript. The general workflow is as follows:

  • Retrieve all events from the delegate calendar that are scheduled to occur today.
  • For each event, check to see if there is an event with the busy title (Step 3).
  • If no event exists, create a new event in the target calendar (Step 2) with the title (Step 3).
  • Return the number of events changed.

Updated March 1, 2022: Added Try block to catch errors when retrieving the start or end date of events.

global delegateCalendarName
global busyCalendarName
global busyTitle

set delegateCalendarName to ""
set busyCalendarName to ""
set busyTitle to "Busy"

on GetPreferences()
	if delegateCalendarName is "" then
		error "No delegate calendar name defined."
	end if
	
	if busyCalendarName is "" then
		error "No target calendar name defined."
	end if
	
	if busyTitle is "" then
		set busyTitle to "Busy"
	else
		set busyTitle to do shell script "echo " & (quoted form of (busyTitle)) & " | xargs"
	end if
	
end GetPreferences

on AddBusyEventsFromDelegatedCalendar()
	set now to current date
	set today to date ("" & (month of now as string) & " " & (day of now as string) & ", " & (year of now as string) & " 00:00:00")
	set tomorrow to today + 60 * 60 * 24
	set num to 0
	
	tell application "Calendar"
		try
			set delegateCal to first calendar whose name is delegateCalendarName
		on error errStr number errorNumber
			error "Could not find delegate calendar."
		end try
		
		try
			set workCal to first calendar whose name is busyCalendarName
		on error errStr number errorNumber
			error "Could not find target calendar."
		end try
		
		tell delegateCal
			set calEvents to (every event whose start date is greater than or equal to today and start date is less than or equal to tomorrow)
		end tell
		
		repeat with e in calEvents
			try
				set startDate to start date of e
				set endDate to end date of e
				
				tell workCal
					set workEvents to (every event whose start date is equal to startDate and end date is equal to endDate and summary is equal to busyTitle)
					
					if (count of workEvents) is 0 then
						
						make new event with properties {start date:startDate, end date:endDate, summary:busyTitle}
						set num to num + 1
						
					end if
				end tell
			on error errStr number errorNumber
				log "" & errorNumber & ": " & errStr
			end try
		end repeat
		
		set theResult to "Number of events created: " & num
	end tell
	
	return theResult
end AddBusyEventsFromDelegatedCalendar

GetPreferences()

AddBusyEventsFromDelegatedCalendar()

NOTE: If you want to change the script to process events for a whole week, change the line:

set tomorrow to today + 60 * 60 * 24

to

set tomorrow to today + 60 * 60 * 60 * 24 * 7

Copy and paste the code above into the Script Editor application on macOS. Then, do the following:

  • Enter the name for the delegate calendar
  • Enter the name for the target calendar
  • Enter the busy event title
  • Save the script

Run the script and see events in your delegated work calendar be added to the target calendar for the current day.

The busy events are added to the target calendar.

TIP: You can hide delegated calendar in macOS Calendar so you don’t see double events at the same time.

Since the target calendar exists on iOS and iPadOS, you’ll see the busy events appear. Huzzah!

Delegate busy events now appear on your iPad and iPhone!

Automating the Script

We will now want to automate the script so it runs periodically throughout the day. Here’s where we run into an annoying oddity with AppleScript. On macOS Monterey, we have a number of native tools to run the script:

  • Script Editor
  • Script Application
  • osascript shell command
  • Shortcuts
  • Automator

Additionally, there are several third party applications that can run AppleScripts:

Check out the execution time to run the script using these methods:

  • Run the script from Script Editor: 4 seconds
  • Run the Script Application: 16 seconds
  • Run the Automator Application: 16 seconds
  • Run the script using osascript from the command-line: 95 seconds
  • Run the script from a shortcut: 75 seconds
  • Run the Script Application from Keyboard Maestro: 18 seconds
  • Run the Script Application using Fast Scripts: 18 seconds
  • Run the script using Fast Scripts: Times out

Method 1: Application

I have no idea why AppleScripts compiled into an application or run through osascript or a shortcut are so slow. We don’t want to manually open the script in Script Editor and run it every time we add a new calendar event, so we will convert it into an application and just accept the slowness. There are two ways to make an application out of a script, and while it’s easier to do it using the Export command in Script Editor, I found problems where I would constantly get infinite permission prompts to grant calendar access to the app.

To avoid this, let’s use the osacompile command line tool to create an application from a script. Suppose the script exists on the desktop with the filename BusyDelegate.scpt.

  • Open Terminal
  • Navigate to the directory where your script is
  • Enter osascompile -o ~/Desktop/BusyDelegate.app ~/Desktop/BusyDelegate.scpt
  • Press Return

The Script Application will be placed on your desktop with the filename BusyDelegate.app. Double-click it to run, and you will see it takes about 4 times as long as running the exact same script from Script Editor. While the application is running, the Calendar app will appear frozen. Once the application has completed the script, control will return to the Calendar app.

Method 2: Script

In testing, I noticed several AppleScript errors when running the application, so perhaps launching Script Editor, opening the file, and pressing Command-R to run the script is a better approach.

Setting up a Cron Job

Next, let’s add a cron Job to run the application on an interval that you choose. I ended up using Keyboard Maestro to create the job, since I’m lazy and didn’t want to muck with my crontab. This KM macro runs the application once every hour on weekdays using the schedule: 0 * * * 1-5.

Keyboard Maestro macro to open the Busy Delegate Calendar application.

If you wanted to go with Method 2, this is the Keyboard Maestro macro you would need to create:

In both cases, the macro opens the last active application. I do this because I don’t want to lose my application focus when the script runs at the top of every hour.

Conclusion

Now I have an automated way to populate my personal calendar with Free/Busy events from my work calendar on my iPhone and iPad. It would be a whole lot better if iOS and iPadOS Calendar just supported displaying availability-only calendars from Google. Maybe it will come in iOS 16 and iPadOS 16. But if it doesn’t, at least I have a workaround. If this tutorial helped you, please leave a comment down below!

7 thoughts on “Syncing Delegated Free/Busy Google Calendars to iOS and iPadOS

  1. Wow, this is much more complex than the Shortcuts I’ve been trying out or trying to create. Good job on that! I’m glad I don’t have any Free/Busy Google Calendars; I didn’t even know there was such a thing.

  2. Mel Obst

    Hello! Can you have two iPads logged onto the same ATEM switcher at the same time with one going between Supersource presets and the other doing other things on the same switcher?

    1. Yes, you can do this. You can also have one iPad with MixEffect running in Split View (i.e. two apps side-by-side) and have them both talking with the same (or different) ATEMs. There is generally a limit of 5 connected clients to an ATEM switcher, so keep this in mind if you are also running ATEM Software Control on a Mac/PC, Companion, etc.

      1. Mel Obst

        Thanks so much! This makes it easy when using one Supersource with a box over slides to have the slidedeck operator running that and keeping the other Supersource for side by side intro boxes. One thing that would be great, but I think it is a Blackmagic thing is putting drop shadow on the boxes. Thanks so much for the great work you are doing! All the Best! Mel Obst

    2. Box Borders and Shadows are only available on the 2 M/E, 4 M/E, and Constellation 8K switchers. The ATEM Mini Extreme and Extreme ISO don’t have those features enabled unfortunately. You can put borders and shadows on the two USK DVEs, however.

  3. David

    Hi Adam, I couldn’t find your contact so I’m reaching out here. I was hoping to discuss some integrations.

    1. Find me on LinkedIn and send me a message there!

Leave a Reply to David Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: