Thursday, June 11, 2015

OmniFocus Push-Triggered Sync

The Omni Group:

Push-triggered sync is a new feature in OmniFocus that uses push notifications to keep all your devices up to date. When OmniFocus receives a push notification, it can silently pull down changes from Omni Sync Server or your own private server, so you’re ready to go the next time you open OmniFocus. This all happens in the background without OmniFocus needing to be launched manually.


After OmniFocus finishes a sync, it asks the Device Registration Server to notify all the other devices associated with its Group ID. The server looks up all the Device Tokens in the group and sends them to Apple, and in turn Apple uses those tokens to issue a push notification to each of your devices. When one of your other devices receives a notification from Apple, it triggers a background sync in OmniFocus that pulls down the changes made on the first device.


To avoid syncing too frequently, OmniFocus includes the Tail Transaction ID in each push request. This randomly generated string identifies the last change made to the server database, so that if your iPhone already knows about the last change your iPad made, for example, the iPhone can prevent a sync and save on cellular data and battery. Transaction IDs include no information about what change was made.

Update (2015-09-23): Tim Ekl:

This way, parsing a configuration file can return one ConnConfig struct for each bundle ID that we’ll use to connect. The provider’s connection code can then iterate over these structs, establishing multiple connections along the way.

Update (2015-10-14): Tim Ekl:

With this implementation, constructing an instance of this logging pipeline component is as simple as calling a function. It needs an existing channel as input, but gives back a new output channel; this means that we can easily chain multiple different components by passing the output channel from one function to the input of another.


For the push provider’s use, we can do a bunch of different things in each of these components – and logging is only the simplest! The provider itself is structured as a pipeline with nearly a dozen components from start to end[…]

Tim Ekl:

Next up, we needed a way to hang on to notification information: what clients were registered with the provider, how they’re grouped, and some statistics tracking. We also needed to integrate with Omni Sync Server for a staged rollout of push: during testing, we enabled push only for some sync servers, in order to measure the kind of extra load that push would levy on our sync system. (Thankfully, this period was very brief, and push is enabled for every customer now.)


The Web portion of the provider was fairly straightforward. Rather than try to wrap Web access in Apache or nginx, or write a separate Web interface that called a push API, we used Go’s built-in HTTP and HTML templating support to handle all incoming HTTP requests and expose a simple but serviceable administrative interface.

Comments RSS · Twitter

Leave a Comment