Univeral Pasteboard iOS, macOS, Android
Do you recall the times when you had to touch-type a long website address on your phone’s baby keyboard and wished to copy it from your computer and continue browsing just by pasting the URL in the address bar? Such a painful and frustrating user-experience. I’ve been doing this for a long time, some people might still be doing this but things have changed on the iOS/macOS side and partially on the Android side and the number of WTFs/day have drastically reduced. With some coding, I can further reduce them down, let’s see.
Apple knew this was a problem so they fixed it with Handoff & Continuity. The iPhone and the Mac are my primary devices but I also like using my Android phone, it has a long list of benefits. I am frequently switching devices for their specific features and use-cases, for all the software tinkering I pick my Android device, for example, I am running a Linux image on my Android phone and on top of it I have setup pi-hole and this pi-hole server is set up as a proxy on my router to block ads and social media trackers on all devices in my home network. This hacking can’t be done on iOS because of the securely closed system. I miss some features of the Apple ecosystem when I am on Android, one such feature is Handoff and Continuity. More specifically, the universal pasteboard, the functionality of copying something on one device and pasting on another, really cool! This seems like a very small feature and indeed it is, but its impact on the user experience is huge. My life will be simpler if I can share pasteboard across all my devices including Android.
#WeekendProject One of the many advantages of being in the Apple ecosystem is Handoff between apps specifically the shared pasteboard. It'll be great if it can be shared with Android as well.— Vinay Jain (@vinayjn7) June 11, 2020
Lets build this
This implementation triggered as an idea in the evening and I rushed to Twitter to post it for validation. From the top it looks like a really complex system to build, and if I go on to implement the full functionality of Handoff and Continuity, it is indeed a difficult software to build. But when given a thought, what I need is a small subset of Apple’s implementation, the universal pasteboard. To build it, I need to know how Apple did it and what is the tech behind. With some little research, I got to know about BluetoothLE.
Handoff & Continuity uses BluetoothLE (bluetooth low energy), a technology that helps nearby devices to communicate using significantly small data packets and low energy. The Bluetooth specification prefers to call the communicating devices as the Central and the Peripheral but in general terms its a client-server communication. The peripheral advertises one or more services & characteristics, which can be found by the centrals with a Bluetooth scan. A central connects to a peripheral with a pairing process which may or may not require authentication.
The serivces and characteristics are important to understand because that is the only way the central can find a peripheral and send data to it. The central will write data on the characteristics which were advertised by the peripherals.
The GATT server
Advertisement of a service is totally different from data transfer. After pairing, the central is authorised to send data to the peripheral without permission. As the central is writing data to a characteristic, the peripheral needs to know how to handle this data. This can be done by setting up a GATT server on the peripheral. The server knows who is writing data on a particular characteristic and exposes certain methods to handle this data transfer. Our shared pasteboard implementaton relies on the GATT server.
For this case, I am interested in getting pasteboard data from Mac to Android. I want my Android device to be discovered by a nearby Mac, you might think that turning on Bluetooth and pairing Android & Mac would do that. Not completely, because Android is advertising the services it supports like file sharing, Bluetooth tethering etc. Luckily, I can use the standard Bluetooth pairing process of Android and don’t have to write any code for the authentication flow. By the time of writing this post, I couldn’t find any advertised services/characteristics that could potentially link to the Android pasteboard. Unfortunately/fortunately, I had to write all the code for advertisement, services and characteristics, GATT server and for the system pasteboard manipulation. This was just the part on Android side, I still need to write code that will send data to Android. Right! the central code on Mac.
Anytime I hit Cmd+C on the Mac I need to send the selected data to Android. I initially thought that there must be an event(NSNotification) fired as soon as the pasteboard contents are updated, unfortunately this isn’t the case.
In computer science there are two ways to get updates from a system:
- You(subscriber) subscribe to the updates on a system(publisher), and get notified for each change.
- You keep polling a system on regular time intervals and compare its latest value with the value you currently have. If it doesn’t match that means the system has a new value.
As I mentioned, I won’t be able to use the publisher-subscriber model(the optimal approach) here, I had to turn my head towards polling. And like most of times I was able to find a solution with a little help from GitHub, thanks to Daemon-Devarshi for their clean Swift implementation. Polling the pasteboard works because the amount of data transferred in polling isn’t that much and I can just ignore the battery related issues for now.
Since I already have the pasteboard updates, writing rest of the central code is relatively easy. Apart from the pasteboard code, the central has the following responsibilities:
- Keep running in background
- Connect to the Android phone when pasteboard changes
- Send data to the connected Android phone
With some help from Core Bluetooth, it took me a little time to write a macOS script that does all that. Just because I did the custom implementation of services and characteristics the central’s code can further be optimised to scan only for the specific services and characteristics I need. And with this it’s done, I have a working solution for a shared pasteboard between all my devices.
I’ll probably be pushing all this code on GitHub after I refactor and clean it but I am not sure when. If you are still interested in looking into it let me know and I’ll speed up the process or may be send it to you directly over email.