Gathered 1.3 has been released and is now available on the App Store. Version 1.3 brings 2 new data sources, app-wide speed and UX improvements, and support for various features added in recent versions of iOS.

This update also has lots of behind-the-scenes changes that will make future updates easier to create and deploy, which – along with my features roadmap – should mean more frequent updates.

I wasn't very happy removing the Heart Rate data source but Apple weren't very happy with the use of HealthKit.

Full Changelog

  • Adds Advertising and Authentication data sources
  • Removes the Heart Rate (via Apple Watch) data source at the request of Apple
  • Data sources can now be reordered
  • Values can now be copied by tapping the cell
  • Adds support for iPhone X
  • Improves layout on iPads
  • Adds drag and drop support for recordings on iPads running iOS 11 or newer
  • Altimeter's "Relative Altitude" value can be reset to zero by tapping the cell
  • Adds "Speed (estimated)" to GPS data source
  • A "Start Recording" Quick Action has been added to the home screen icon
  • Recordings will now always use the update frequency set in the Settings tab
  • Fixes some exported CSV files being invalid
  • Fixes the Microphone data source pausing other audio
  • Fixes a crash that may occur when stopping a recording

Sharing a location on iOS is something that not a lot of apps need, but after requiring it for my latest app, Scanula, I found that there isn't a good resource explaining how to do it properly. This is the first post in a series of planned posts going over a few of the tips, tricks, and common pitfalls I have found while working with iOS Share Sheets.

Sharing on iOS - An Overview

Standard iOS "Action" Icon

Sharing on iOS is done using the Share Sheet, which is often opened via the "Action" icon (shown left). When tapping this, the user is presented with a Share Sheet, which provides various options, depending on the item being shared. In the blog post we'll be looking at location exclusively, but there are a various things that can be shared, from images, to URLs, to text files. The full list can be found in Apple's Documention.

UIActivityViewController and UIActivityItem

There's not a whole lot of documentation that helps determine exactly how to use UIActivityViewController and its associated classes, but the simplest way to use it would be:

// This code assumes:
// - This is inside a subclass of `UIViewController`
// - This is inside a class that contains a property called `shareBarButtonItem` of type `UIBarButtonItem`

let activityItems: [AnyObject] = [
    "A shared piece of text"

let vc = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)

// If run on iPad, this is required
vc.popoverPresentationController?.barButtonItem = shareBarButtonItem

presentViewController(vc, animated: true, completion: nil)

This would present a Share Sheet sharing "A shared piece of text". This would allow the user to share this text via various built-in applications, such as Messages, Mail, or Notes, via 3rd party apps, such as Dropbox or Facebook, or via AirDrop. On its own, this isn't particularly useful, but it's a start.

Note that UIActivityViewController's designated initialiser takes in an array of AnyObject. This may look like an open invitation to just pop anything in the array, but it's actually far from that. Even though it's smart enough to figure out what you want with simple things (such as text, as shown in the example), it cant infer what you want to send for all items.

For more complex items, items should conform to UIActivityItemSource (something I hope to write another blog post on). However, in this case, simply using NSItemProvider will help a lot.

Sharing Locations

The primary focus for this blog post is going to be sharing locations. So, without further adu, here's the meat to go with your potatoes.

The "Copy/Paste From StackOverflow" way

When searching Google for "uiactivityviewcontroller share location", the top results (I've not checked all ~150,000) point to a very similar solution:

  • Create a VCard containing the location
    • Note that some solutions suggested creating an contact via the AddressBook framework and using that to generate the VCard contents, crazy!
  • Write the VCard data to a temperary location on disk
  • Pass in the NSURL of the file

Here's my example code:

// Note: Don't use this code!
func activityItems(latitude: Double, longitude: Double) -> [AnyObject]? {
    var items = [AnyObject]()

    let locationTitle = "Shared Location"

    let locationVCardString = [
        "PRODID:-//Joseph Duffy//Blog Post Example//EN",
        "item1.X-ABLabel:map url",

    guard let vCardData = locationVCardString.dataUsingEncoding(NSUTF8StringEncoding) else {
        return nil

    let fileManager = NSFileManager.defaultManager()
    guard let cacheDirectory = try? fileManager.URLForDirectory(.CachesDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true) else {
        return nil

    let fileLocation = cacheDirectory.URLByAppendingPathComponent("\(latitude),\(longitude).loc.vcf")
    vCardData.writeToURL(fileLocation, atomically: true)

    return [

While this does technically work for most use cases, when sharing via AirDrop the items is interpreted as a file (as it techncially should). This has some unwanted side effects:

  • Some apps that should be able to share a location (such as Facebook's Messenger) see the item as a file and refuse to share it
  • When sharing via AirDrop, the item is shared as a contact card, and the user is prompted to add the contact the their contacts, not view the location
  • The information is written to disk, which while not being a big deal, can be avoided, so why not just keep it in memory?

Lead By Example - The Apple way

When trying to figure out the correct way of doing this I created a smalled app for debugging Share Sheet items (hopefully more on this in another blog post). This shows me that Apple's built-in Maps application does things a little differently by sharing:

  • A single text item (the title of the location)
  • An Apple Maps NSURL
  • The location in the form of a VCard, but simply as NSData (not stored in a file)

Doing this is fairly easy. Here's my code to do it:

func activityItems(latitude: Double, longitude: Double) -> [AnyObject]? {
    var items = [AnyObject]()

    let locationTitle = "Shared Location"
    let URLString = "\(latitude),\(longitude)"

    if let url = NSURL(string: URLString) {

    let locationVCardString = [
        "PRODID:-//Joseph Duffy//Blog Post Example//EN",
        "item1.X-ABLabel:map url",

    guard let vCardData = locationVCardString.dataUsingEncoding(NSUTF8StringEncoding) else {
        return nil

    let vCardActivity = NSItemProvider(item: vCardData, typeIdentifier: kUTTypeVCard as String)



    return items

This doesn't require much more code, but has a few other added bonuses:

  • When shared via AriDrop, ipens
  • Allows sharing via apps that don't support sharing file, such as Facebook's Messenger
  • Allows the user to copy the location to the clipboard in form of "<url> <share title>"

I've been doing a lot of work with Share Sheets lately, so if you've found this post useful and want to see more, check back soon, subscribe to the RSS feed for this blog, or follow me on Twitter.

This blog post covers an open-source timetable parsing project I released a couple of months ago. It is available at and the source is available on GitHub. The post won't go too in-depth on the technical side of the project, but rather the story of how I discovered it was possible.

Since starting my studies at the University of Huddersfield I've always wanted an easy way to see my timetable on my phone. The timetable available on the website isn't responsive and relies on POST data to display future weeks timetables, 2 things that don't work great on mobile, especially when the page is kept open in the background.

To get around this I would manually add each of my lectures and practicals to my calendar. These events could be set as recurring, however they would often need removing on specific days (such as during holidays) or have different information on another date, such as a room change. All of this eventually led me think about the famous XKCD Automation comic, so I started work on a method of automating adding it to my calendar.


My first idea for how to automate the process was to create a Google Chrome extension. To try and figure out if this was possible I loaded up my timetable.

Here I noticed that the URL didn't have any information about the timetable. My next thought is that my student number must be stored in a cookie. So I opened up the Developer Tools to inspect the request. To my surprise it was a POST request... with the student number as part of the form data.

But surely it would be just be using that to validate that I was the user I said I was, right? I loaded up my favourite HTTP utility, DHC, and created a basic request to load my own timetable.

It worked! Just to double check I messages one of my friends explaining the situation and asking him for his student number. He sent me the number and, again, it worked! My first thought was that I was happy that I'd found a way that I might be able easily scrape the data I needed. My second thought was that it was a bit worrying that by only knowing someone's student number you could find out where someone was likely to be. Despite this I started thinking of how I could truly automate this.

EaaS - Exploitation as a Service

Since it was so easy to access the data I thought it'd be a good idea to make the service available to others. Creating a Google Chrome extension would prevent some users from using the service and could make it a little harder to get it in to a user's calendar. The calendar would also not automatically update. The overall user experience would be worse.

Look at the state of this place!

Getting the current weeks timetable is easy, but what about future weeks? To figure this out I loaded up my timetable again and changed the value in the "Week beginning" dropdown.

As you can see, there's a bit more going on this time. However, having worked with ASP.NET before, I can see that it won't be too hard to make the request work. So I make another request:

Now we have where people will be for the rest of the academic year all of my future timetables!

Any application that can be written in JavaScript, will eventually be written in JavaScript

As per Atwood's Law, any application that can be written in JavaScript, will eventually be written in JavaScript, so naturally I turned towards Node.js.

I stuck with Express and found jsdom, a lovely framework for working with a DOM on the server. This would then allow me to pull the information and traverse the DOM on the server side. It might not handle errors too well but my timetable's markup doesn't appear to have changed since I started University, so it'll do.

Since I've got the DOM on the server-side to get future timetables I can simply take the value from the hidden inputs __VIEWSTATE and __EVENTVALIDATION and send them with the request. Simple!

Who doesn't love a good RFC?

Now came the tedious part: extracting the data and converting it to a format that calendar applications will understand. I've created single calendar events in the past, but never a full calendar with lots of extra fields, such as the VALARM. Overall the iCalendar specification is rather long and complicated, but it's easy enough to focus on the parts needed for the project. Primarily I had to ensure that events would trigger at the right time, independant of time daylight saving time, which means adding ;TZID=Europe/London to all event dates.

Add a couple of options for adding alarms prior to events and set the correct Content-Type headers and you're set! There were a few kinks to work out but I've been using it for a few weeks now and love it.

What you don't know can't hurt you

Before I released the code or created the website I spoke to one of my lecturers to ask whether he knew if I was breaking any rules. Apparently he (along with other members of staff) has told the team responsible for the timetable website about the security issue and they've decided not to do anything about it and essentially ignore the problem. That's up to them, but personally I think it's a little creepy that someone could make a website where people can view anyone at the University's timetable. But who'd do that?