The Apple Maps Collections URL Scheme
Apple has created a URL scheme to share Apple Maps collections without storing any server-side data. All information necessary to display the collection is stored in a protocol buffer in the URL itself. I’ll show you how to decode the data stored in a collection url, and dig into what it means.
In 2019, Apple maps introduced collections: sharable lists of locations in Apple Maps. iOS natively supports collections, so sharing between iOS devices provides a seamless experience. But Maps.app for macOS doesn’t yet support collections at all. So sharing a collection from iOS to macOS opens a web page with a URL like this:
https://collections.apple.com/collection?_col=ChkzNiBIb3VycyBpbiBQb3J0bGFuZCwgTWUuEl4aNVdlc3Rlcm4gUHJvbWVuYWRlLCBQb3J0bGFuZCwgTUUgIDA0MTAyLCBVbml0ZWQgU3RhdGVzIhIJvol88gvTRUARJ62u%2BaeRUcAqEVdlc3Rlcm4gUHJvbWVuYWRlEg0Irk0Q%2Bsvey9ivxpsPEg4Irk0QyuH475jurPioARJdGjAxODEgU3RhdGUgU3QsIFBvcnRsYW5kLCBNRSAgMDQxMDEsIFVuaXRlZCBTdGF0ZXMiEgmReYlWk9NFQBHVlwBXDZFRwCoVT25lIExvbmdmZWxsb3cgU3F1YXJlEg0Irk0QmarQ4r6omMpiEg4Irk0Qnojwv%2BmwwYjjARINCK5NEMX3iPvSiK7%2BEhIOCK5NEPiehseY6IrDgAESDQiuTRDhnL69jcilpF8SDgiuTRCUtvKfpI%2FujJMBEg0Irk0Qlu2S5PP6wbAaEg0Irk0Qta7tvbWs16w8Eg4Irk0QyprGyqzzuamOARINCK5NEKK7ke%2FBoJyfIhIOCK5NEPPmsNixr4awzwESDQiuTRC3pN3hgdTCjG4SDgiuTRDZ0KWnlan9o5sBEg4Irk0Qz5Dv76HQub%2FdARIOCK5NEKbF7OSsgomCrgESDQiuTRCXk%2Fmpq4icq3oSDgiuTRCu2LSXt8ze3aQBEg4Irk0Qxr%2FOvs%2Bz%2Bo7rARIOCK5NEI7Ko4r1j86osAESDQiuTRDhqoT2tLCayEMSDgiuTRDR38Pkz%2BWjzdEBEg0Irk0QqoS1hc23pPUzEg4Irk0QybPZmtGxzK2YARINCK5NELudnqabgaWNHA%3D%3D
That is one grotesque URL! It is so long because Apple Maps does not require any server-side data to display a shared collection; this URL contains a protocol buffer containing all data necessary to reconstitute the collection. A protocol buffer is a highly compact data serialization format developed and open-sourced by Google(!). It’s a fantastic tool to squash a dictionary-like data structure down into a very small number of bytes. The algorithm to extract the collection data from the above url is as follows:
- URL decode the
_col
query parameter - Base64 decode the resulting string
- Parse the resulting binary data as a protocol buffer with the following format:
Below is a simple python script that will print out data contained in the _col
parameter of the Apple Maps collection URL above. It assumes I’ve compiled the above protocol into a python module named maps_pb2
:
This program will print out the following data structure:
name: "36 Hours in Portland, Me."
location {
address: "Western Promenade, Portland, ME 04102, United States"
coordinates {
latitude: 43.6488021
longitude: -70.2758774
}
name: "Western Promenade"
}
...
location {
address: "181 State St, Portland, ME 04101, United States"
coordinates {
latitude: 43.6529339
longitude: -70.2664392
}
name: "One Longfellow Square"
}
location {
lsp: 9902
appleMapsId: 7103409456625751321
}
...
location {
lsp: 9902
appleMapsId: 2025093751865052859
}
The data structure is pretty simple: it contains the collection name, and then a location
structure for every saved location (i.e. pin on the map) in the collection. However, the location structures take two forms: one with a location name, address, and coordinates:
location {
address: "181 State St, Portland, ME 04101, United States"
coordinates {
latitude: 43.6529339
longitude: -70.2664392
}
name: "One Longfellow Square"
}
The other just has an Apple maps and some field that I’m calling lsp
*:
location {
lsp: 9902
appleMapsId: 2025093751865052859
}
When constructing a collection, you can either add existing Apple Maps locations, or you can “drop a pin”, name your custom location, and add that. In the former case, your location will just be stored with an id; in the latter, you’ll have the full record stored in the location structure.
These apple map IDs can be accessed by plugging them into Apple Maps URLs like so: https://maps.apple.com/place?auid=APPLE-MAPS-ID
Apple Maps collection URLs provide another data point in Apple’s continued effort to protect their user’s privacy. There is no personally identifying information in the collections URL scheme. Once shared, it is impossible to determine who originally authored a collection. Very ugly URLs, but a pretty attractive user experience.
I’m also encouraged that the Maps team chose to use protocol buffers, a Google open source project, at all. It’s a another great sign that NIH syndrome antibodies are flowing around Cupertino.
* Serialized protocol buffers (like the _col
parameter in the collections URLs above) do not contain any information about what the fields within them are called. So the protocol definition above is entirely a work of reverse engineering: I have no idea that the field address
is called actually address inside the Maps application. That said, all fields in the data structure had a pretty obvious meaning except for a mysterious integer, 9902
, that always appeared when a location was described only by an id.
But if you share you an individual location from Apple Maps, the generated URL always contains the query parameter lsp=9902
. So I called the mystery field in the protobuf the same thing: lsp
. I’m dying to know why this field is here and what it means; if you’re willing to fill in this blank, please do get in touch.