Migrating to AWS EC2 and a APNS Server

“The last time I used Ctl-c to get out of trouble was
on a PDP-11 at high school.”

As we move closer to the release of Loiter, we need to move away from our very modest development server to something more robust and scalable. We choose Amazon AWS EC2. While Amazon offers a service called LightSail — a preinstalled LAMP stack on what is essentially is a EC2 server — we went with the economy, flexibility and scalability of a vanilla-flavour EC2 server; T2-size to start with.

Another reason we needed a raw LAMP stack was to run a bespoke Apple Push Notifications Service (APNS) server. Having not been able to find a suitable outfit that offers an APNS backend for start-ups like us, we decided to write one ourselves – in C – I mean how hard can it be..!? Up to this point our APNS notifications were handed in the individual server-side PHP files, with each individual client-initiated PHP call opening and the closing a connection to the APNS gateway. While doing that a couple of hundred times a day on the sandbox gateway during development might be acceptable, doing this post-release on the production APNS gateway would very quickly be deemed a Denial of Service (DOS) attack and our IP address would be banned (as Apple explain here).

We choose to go with an Amazon Linux AMI instance, for no other reason than this flavour of LINUX seemed to be very well supported by Amazon with lots of online tutorials,  guides and documentation.  For instance these step-by-step instructions for installing a LAMP stack on the server are excellent.

PS : As a post script to this post, here is a short list of “tricks” that we learned along the way, many a result of  doing our LINUX/C development on a Ubuntu box and then having to transfer it all to this(Red Hat-like) Amazon Linux AMI instance.

#1. Change permission on the key pair PEM file that is used to secure your SSH to your EC2 instance.

 chmod 600 MyKeyPair.pem

#2. Set the Apache server to start automatically after the machine reboots:

 sudo chkconfig httpd on

#3. Give Apache ownership of directories into which you want it to save upload files. We use this to upload Loiter user profile photo and location photos :

sudo chown apache myUploadDestinatioDirectory

#4. Our APNS server reads Loiter notifications from a MySQL database and sends them to the APNS gateway using SSL. The server is written in C and required installing both the OpenSSL and MySQL C API libraries.  In the end this was straight forward:

sudo yum install libssl-dev
sudo yum install mysql-devel

Discovering where exactly the include files and object libraries had been installed was a challenge. Here is the gcc build command that worked:

gcc -Wall -std=c99 -o "apnsServer" "apnsServer.c" -L /usr/local/ssl/lib -lssl -lcrypto -I/usr/include/mysql55 -L /usr/lib64/mysql -lmysqlclient -lpthread -lz -lm -lrt -ldl

The -std=c99  switch  is there so that the C code can declare variables within functions,  such as for‘ loop initial declarations like this:

for(int i=0;i<nCount;i++)

#5. We need our APNS Server to run in the background like a service (and not quit when the SSH terminal session closed. So nohup with the final & did the trick

nohup ./apnsServer > apnsServer.log 2>&1 &

The > apnsServer.log directs any stdout to this log file and the 2>&1 does the same for any error messages.

The server process and its PID can be rediscovered in subsequent terminal sessions using

pgrep apnsServer

and can be terminated ungracefully using its PID number:

kill -9 <PID>

Currently we are working on a method for automating the monitoring of the APNS server service and leaning towards using monit. I’ll do a blog post on that is it turns out to be usefully interesting.

Changing iOS App Icons Programmatically

When using Loiter to post your location to your friends, the app keeps track of where you are, and when you leave the posted location, the post is ‘expired’ and a background notification is sent to your friends. On your friends’ devices, your location marker changes from dark blue to grey to signify that you are no longer at that location.

To be able to ‘expire’ posted locations, Loiter asks users to allow the app to keep track of their location even when Loiter is running in the background. Once the user’s ‘live’ location is expired, Loiter stops monitoring the user’s location. That saves batteries and minimises any concerns about privacy.

To further alert users to what is going on, we thought that it would be nice to change the Loiter application icon to indicate when the app is tracking a user’s position in the background. Something relatively inconspicuous like a red dot in a top corner of the icon, or some such thing.

Well, it turns out that with iOS 10.3 it is possible to change an application’s icon during runtime. But unfortunately the implementation is poor, as we shall see.

To change an application’s icon in runtime, first you need to define the alternative icon in the app’s info.plist file using the CFBundleAlternateIcons key. Here we have two icons defined: IconNormal and IconBackground.

<key>CFBundleIcons</key>
<dict>
  <key>CFBundleAlternateIcons</key>
  <dict>
    <key>IconNormal</key>
    <dict>
      <key>CFBundleIconFiles</key>
      <array>
        <string>AppIcon.appiconset</string>
      </array>
    </dict>
    <key>IconBackground</key>
    <dict>
      <key>CFBundleIconFiles</key>
      <array>
        <string>icon_background</string>
      </array>
    </dict>
  </dict>
</dict>

Notice that IconNormal defines an appiconset as included in the project’s .xcassets file. But for the second icon, it does not seem that you are able to define alternative icons using icon sets. Instead the CFBundleIconFiles array seems only able to take only one PNG icon file – here “icon_background.png”. This is unfortunate as that one PNG will be re-rendered by the OS for all other resolutions, and so image quality will be suboptimal.

Once you have defined the alternative icons, changing the application icon is simplely a matter of calling setAlternateIconName on the singleton app instance, as follows:

  [[UIApplication sharedApplication] setAlternateIconName:@"IconBackground" completionHandler:nil];

All well and ok. However, somewhat bewilderingly each time you change the application icon, an alert stating that “You have changed the icon for ‘AppName’” is displayed!

have changed the icon for

Why? Who is “You” here? Why does Apple feel that the user needs to be alerted when the application changes its icon? I understand that app icons changing haphazardly might detract from the user experience, but surely it is in the interests of developers themselves to avoid confusing users by doing this. An implementation like we have in mind, whereby a small change to the app icon (superimposing a small red dot, for example), indicating some change of status, would be totally unobtrusive and acceptable, and surely does not warrant displaying a modal alert.

While a modal alert renders this feature all but unusable in a consumer application, there is a further issue that makes it useless for our purposes. As outlined earlier, we’d like the icon change to indicate that Loiter has stopped monitoring a user’s position in the background. Using CLLocation’s background location updates, when a user moves sufficiently far away from a posted location, a UIBackgroundTask (calling beginBackgroundTask) expires the post, stops the background location updates, and would change the app icon.

Disappointingly, this does not work.

Here is how we tested it:

UIApplication *app = [UIApplication sharedApplication];
    
  __block UIBackgroundTaskIdentifier bgTask;
  bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        
    [app endBackgroundTask:bgTask];
  }];
    
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 
    NSLog(@"Started background after 3 seconds");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC), dispatch_get_main_queue(), ^{

      [[UIApplication sharedApplication] setAlternateIconName:@"IconBackground" 
                                         completionHandler:^(NSError * _Nullable error) {
                
        NSLog(@"Error... %@",error);

      }];
            
      [app endBackgroundTask:bgTask];
      bgTask = UIBackgroundTaskInvalid;
    });
});

The UIBackgroundTask is set up and uses Grand Central Dispatch to create a secondary thread to handle the background task. Then we again use GCD to call the main thread after a 3 second delay (time to put manually the application into the background) and call setAlternateIconName. User interface changes in iOS, including changing the app icon, must be made on the main thread.

When run in the foreground, this code runs fine – the icon changes and the (annoying) modal alert appears. However in the background, the code throws the following error :

   Error Domain=NSCocoaErrorDomain Code=3072 "The operation was cancelled."

I don’t really understand why this happens, but it may have something to do with the fact that the UI (icon) update needs to be done on the main thread, but executing as a background task doesn’t permit this jump to the main thread, forcing setAlternateIconName to run on the secondary thread, and therefore it fails.

So in conclusion, unfortunately, with the intrusion of a modal alert and the inability to change the app icon from a background task, the iOS 10.3 implementation of programmatically changing an iOS app icon is not usable for our purposes. I really hope Apple rethinks this API and makes it useful to developers, even if they have to impose editorial restrictions on its use. As it is, I can’t see it being of any use in any customer-focused application.

Buying the domain name Loiter.com

Domain names and matching social media handles are the position, position, position of internet real estate. And just like brick and mortar,  the good positions have mostly been taken and are not cheap acquire. I have been asked how I managed to get my hands on the domain name loiter.com. Well, the short answer is I had to spend a far bit of money.

These days, short (3-, 4- 5- and 6-letter) domain names that use real english words don’t go cheaply. A quick search of brandbucket.com shows that 6-letter domain names sell for in the range of $300 000 (for sketch.com) to around $10 000 for less ‘realistic’ or compound words like “highiq.com”.

On top of that, it seems that certain techie domain names fetch substantially higher prices. For instance, before coming up with loiter.com, I considered pingme.com which I saw was pointed at a rather nondescript parking page so I assumed it was up for sale. Using whois.com I found the owner’s contact details and here is the rather brief email exchange I had with a certain Pete:

Me: Hi. Are you interested in parting with pingme.com?
Pete: Yep. Are you interested in parting with $500k USD?
Me: Yikes! And this is where we part ways. Thanks.

In the end I paid €12 500 ($15 425) for loiter.com. That’s about the price of a brand new (admittedly low-spec) one of these:

The domain was offered for sale through a broker called Sombrero.de without a “list price”. You are asked to make an enquiry by making an offer. I started at what I know was a very low offer of €2 750. In brief, here is how the negotiation unfolded over the course of about one week:

Me:    €2750
Them:  €18000
Me:    €8250
Them:  €15000
Me:    €12500
Them:  €12500 OK its a deal.

The sombrero.de fee for their escrow service was 3% so the total cost of acquiring loiter.com was the princely sum of €12 875 (or around $15 872). That goes on the company balance sheet as an intangible asset funded by a capital injection from me.

In addition to domain names themselves, there is also the additional exercise of trying to get the matching handles on Twitter, Facebook, Instagram etc.  This is somewhat more complicated.

In the case of Facebook, the @Loiter handle and corresponding www.facebook.com/Loiter page is owned by a Chinese lady who seems to sell jewellery and other decorative nic nacs  online and at a market stall. The last post on the page was in December 2016. I am not sure whether Facebook allows the sale and transfer of such pages, but I have not had a response to my direct message to the owner about buying the page.  In the interim I have been using www.facebook.com/loiterwhereyouare.

It is a similar situation with Twitter where the handle @Loiter is owned by what appears to be a now defunct clothing brand called “Loiter Apparel….”. Their last tweet was in October 2009. Again I tried to track down the owner with no luck. I also contacted Twitter support and @Jack replied:

Hello,
At this time, we are unable to accommodate individual requests for inactive or suspended @usernames. 
Thanks,
Twitter Support

So I guess that is that. Like Facebook, I am using @LoiterWhereUR as the Loiter twitter handle, though admittedly with limited success so far – the account has 65 followers and rarely gets a like or retweet to a post. Our social media marketing has a long way to go, especially given the intangibility of the thing I am trying to promote.

Crossed Paths : a Loiter side project

Over the past 10+ months, while developing Loiter, I’ve taken a couple of breaks. One was for the birth of my daughter Ella in December. During these breaks I wrote and released two iPhone apps, based on ideas I’d had percolating for a while.

The first is Crossed Paths, based on an idea that my friend Kaja (the owner of a fantastic little hotel in Rome called Salotto Monti) and I had a couple of years ago. The idea was for an app that that would trawl through all the personal info that two people have on their respective phones (and perhaps elsewhere) and discover if, when and where these two people might have crossed paths in the past. We thought that it would be a great conversation starter.

There are already a couple of apps in the iPhone/iPad app store that do something like this but use the location data in your social media accounts. Perhaps the most popular is a dating app called happn which apparently also keeps track of your position and matches people based on shared locations. There is also already an app called Crossed Paths in the appstore that uses your Facebook account to harvest and match location data, though I have not been able to get it to work.

(Incidentally, I am not sure how it is that Apple allowed me to use exactly the same name as the existing app (Crossed Paths) when I initially submitted my app. I had thought that the names of apps had to be unique. Is the only possible explanation that the author of the app used a special (non-breaking?) space character between the words?)

So finally in January I decided to give it a go. Unlike those other apps, I decided to focus solely on the metadata available in the photos on people’s phones.  As you probably know, when we take a photo on our phone, the time and location the photo was taken (as well as a lot of other information about the photo) is also saved as “metadata”.  And indeed, given the number of photos people have on their phones these days (I have over 13 000 on my phone for example) that seemed to me a significant number of data points to start with.

The iPhone operating system, iOS, makes it quite easy for developers to access this photo metadata data programatically, as long as the user gives the app permission to do so. Additionally, even if the photo has been moved off the phone into iCloud, the photo’s metadata is retained on the phone itself. This makes accessing and processing the data both straight forward and fast.

The first issue was how to “connect” the two peoples’ phones. Given that I envisaged two people, who had just met, sitting in a bar, looking for something in common to talk about, I assumed that the phones would be in close physical proximity. As such the options were (i) local networking (like bluetooth for example), or (ii) requiring the users to create accounts and then share user names , or (iii) using the phone’s camera and QR codes. I didn’t want to go down the route of requiring user accounts. My experience with Loiter has made it abundantly clear to me the very large overhead of coding and managing user accounts.  While close-proximity networking is attractive, it can be cumbersome, not to mention unreliable, and I had not yet discovered the “delights” of Apple’s MultipeerConnectivity framework. Moreover, the visceral nature of two people sitting side-by-side and scanning QR codes on their respective phone seemed attractive to me. So QR codes it was.

So the process of using Cross Paths is a follows. A user downloads the app. On first starting, the app asks for access to his/her photos. Having been granted permission, the app  scans through all the user’s photos and creates a time/location summary  data file which is then uploaded to our server, identified only by 64 character ID code. It is this code that the QR code presents to the second person. When the second person scans the QR code, the first person’s time/location data file is downloaded and a multithreaded process of looking for matches starts. The data file isn’t too large so download times are quick. For example, for the 13 500 photos on my phone, the compressed data file is 436 KB, which downloads in a few seconds over 4G.

The time/location matching analysis is presented to the user in four ways.

 (i) A map showing the location of all the photos each person took in a particular month – red for you and blue for the other person.

Your (but not the other persons’) icons can be tapped to see the corresponding photo and photos close by (within 100 meters).

(ii) A heatmap showing overlapping locations (regardless of when) – that is approximate locations that both users have in common. (The city labelling that is shown in this screenshot is still being worked out in the actual app.)
(iii) A list of cities that both people have visited, regardless of time.

 

(iv) A list of actual crossed-paths : that is places that both people where on the exact same day, including how far away from each other they were!

Because of the multiple modes of analysis, the app uses multithreading extensively, but that was quite straight forward to code given the independence of the processing : once the second person’s data is downloaded from the server and uncompressed, the processing of each mode can progress independently and the presentation of the results is made available (the buttons are enabled) once the relevant thread finishes.

The common-cities mode is based on a database of 7 200 cities. Bounding coordinates for each city were calculated based on the city’s population and parameters that I estimated using some rudimentary econometrics. Searching through this cities database for matches is very processor intensive and had to be written in optimised C++ rather than Objective-C for maximum speed.The optimal longitudinal ordering of the cities’ boundaries in the database also improved efficiency enormously. These optimisations improved processing time by one and a half orders of magnitude (15x).

The maps in the app are done using the fantastic Google Maps API for iOS for which, incredibly, unlimited use remains free for developers of apps such as Crossed Paths. The heat-mapping is done using Google’s utility library.

Privacy was a major consideration when designing the app. When two people use the app, absolutely no identifying information is passed between phones. The unique 64 character ID code contains no identifying information. And indeed the other person’s location data that is downloaded from the server is not saved on the first person’s phone. It is held in memory, processed and then immediately discarded. Of course, none of the second person’s photos or other extraneous person information is transferred to the other device.  Here are the first few lines of the app’s Privacy Policy:

Our Privacy Policy – the bottom line : we do not collect or store any personal information while you use ‘Crossed Paths’ nor is any of this information collected or stored on the pairing device – full stop.

There is therefore no way you could be identified individually, or your individual location determined, both present and in the future. Moreover, none of your person information, photos or any other related data is saved on the device of the person you are pairing with to use ‘Crossed Paths’. In no way can Loiter Ltd., or the person you are pairing with use ‘Crossed Paths’, to identify, contact or locate you based on you using the software or pairing with someone else using the software.

The app runs on both iPhone and iPad and is free from the app store here.

Receiving a friend’s location in Loiter

We have recently completed the addition of a photos feature to Loiter. Now, as well as posting a location and a message, friends can share photos taken at the location they are posting from. Only a photo taken there and then can be attached to the post.

Here is an example of Kaito posting from Asakusa in Tokyo, along with a photo of the cool street-side bar where he is having a drink, while I’m in London, around 10 000 kms away…

Welcome to the brand-spanking new Loiter Blog!

Of course, I’ll be discussing Loiter a lot in this blog but just a brief introduction: Loiter is a soon-to-be-released location-based social networking app that allows users to share their location, post photos, recommend places, organise meet-ups with their network of friends (and more).

And so who am I? Well, my name is Peter Vujanovic. I am the founder, CEO and currently the everything else, including software developer, of Loiter Ltd. I have spent most of the past 10 months bringing the idea of Loiter to life. It has been a blast – the first six month in Paris and now in London. But the time to release Loiter into the wild is fast approaching, and this new blog is me sticking my head above the parapet and girding myself for the big day.

In the blog I intend to share my experiences in promoting Loiter, as well as posting news and other relevant Loiter updates.

photo: The Sheikh Zayed Mosque, Abu Dhabi.