FATCA – FFI List – Version 2

I started rewriting FFI List pretty much the day after iOS 8 was announced. Version 2 is a significant release and I’d like to discuss a few of the bigger changes in this blog.


I am, along with many other developers, still learning Swift and it’s a challenge I am relishing. That being said, I am pleased (nervous!?) to say that FFI List is now entirely written in Swift with the exception of Apple’s Reachability code and the Parse 3rd Party library.

FFI List

In Version 1.x of FFI List, updated lists were only distributed with app updates. That wasn’t ideal for a variety of reasons:

  • There was a delay between an FFI list being released and it actually being available in the app; and,
  • Review scores were reset with every release of the app.

With Version 2, that’s changed. The app is seeded with an FFI List (August 25th 2014), and new lists can be downloaded when available. The September 24th 2014 list is available to download now.

This was a significant piece of work for the app. (I hope it works well.)

User Interface

The app UI now supports all devices from iPhone 4S to iPhone 6 Plus, iPad 2 to iPad Air, in both landscape and portrait orientations. What’s more, the app now supports dynamic text. So if you use larger text sizes, the app will display data in larger sizes as well. The Avenir font has also been replaced with Helvetica.

I also wanted to simplify searching. Instead of specifying what you're searching for (name, GIIN, or domicile), you just need to input your search text and the app will search all three. A small change, but one that reduces the number of taps.

FFI List can be downloaded here

Thoughts on the Apple WATCH

Apple really pushed the health and fitness aspect of their new WATCH. It misses one thing that I find essential: standalone GPS. For a runner out on a run, or a cyclist out on a cycle, GPS tracking is of huge importance and I can't believe the WATCH has missed it. You need to have your iPhone with you to use the GPS functionality.

If I want to run without music, I take my Garmin Forerunner which allows me to track my run and my heart rate (with the additional sensor). If I want to run with music, I run with my iPhone on my arm1 (also allowing me track my run and my heart rate (with a Polar H7)). There’s no use case in the middle that makes sense for an WATCH: it needs its own GPS chip to be of use.

WATCH reminds me of the original iPhone: it had no GPS and couldn’t connect to mobile networks, but was still a revolution in terms of what to expect from its market segment. WATCH is the same. It will get there, but this version has early adopter written all over it.

  1. I have no idea what running with an iPhone 6 Plus will look like. I’m keeping an old iPhone 5 around just incase.

Protocol Updates in Xcode 6 Beta 7

Xcode 6 Beta 7 was released yesterday evening and brought with it a host of minor protocol updates, some of which broke all my existing UITableViews stating that they no longer conformed to the UITableView protocols.

After moving code around, deleting derived data, and generally pulling my hair out, I eventually noticed what changes had been made.

Old Protocol

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!

New Protocol

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell

Almost identical, but looking at the release notes:

"A large number of Foundation, UIKit, CoreData, SceneKit, SpriteKit, Metal APIs have been audited for optional conformance, removing a significant number of implicitly unwrapped optionals from their interfaces...These changes replace T! with either T? or T depending on whether the value can be null or not null, respectively."

Importantly, you will only see error messages where the protocol function is required. Where it is an optional function, no error message is presented.

App Store Rejection Stats

Apple’s Top 10 app rejection reasons make for interesting reading.

6% of apps submitted were rejected for having user interfaces that were either complex or less than very good.

I have no issue with Apple being the gatekeeper for the App Store, but the above rule is so vague and subjective that it must be impossible to police consistently. I am surprised none of my apps have been rejected for this reason.

That said, I have been rejected once:

20.4 Apps that allow a user to directly purchase a lottery or raffle ticket in the app will be rejected

I was submitting a bug fix1 for Lucky Dip when the above rule was flagged to me.

Lucky Dip picked lottery numbers for you and allowed you create a correctly formatted text message which could be sent to the National Lottery which then purchased your ticket. All purchasing happened outside the app and no account or payment details were ever stored in app.

Despite appealing to App Store Review Board that you couldn’t actually buy a ticket in the app, but instead could only create a text message with the correct numbers, my appeal was rejected.

How did I get around it? There were two buttons: “Pick Numbers” and “Play Numbers”; I changed “Play Numbers” to “Create Text” and the Review Board were happy.

N.B. It turned all of this discussion ended up being a minor waste of time. The National Lottery discontinued the Play by Text service shortly after I realeased Lucky Dip.

  1. This is what annoyed me the most about this rejection: no new functionality was being added and the app had lived quite happily with previous bug fixes.

Off Topic: Long-Distance Running

A Brief History

I used to hate long-distance running.

When I was in school I thought it was some form of torture. It was arduous, boring and seemed (at the time) to be a gap-filler for PE lessons. No skills were involved, no team-vs-team competition; you just ran, continually.

At university those thoughts were amplified because treadmills were the running implement of the day (if you went to the gym). Running long distance on the same spot was (and remains) a horrible experience for me.

After university, I took to cycling long distances ranging from 30 - 125 miles on weekend cycles. When I emigrated from the UK to Singapore my bike never made the trip. The roads in Singapore are always busy - there’s not the same opportunity for cycling in the country on quiet roads.

Back to Running

As a result, I gingerly returned to long-distance running. To be accurate, I returned to short-distance running: every second morning I’d go out for a 2km - 3km run and kept that up for about a month before increasing the distances to 5km, then 7km.

I’ve just noticed that this is when I started using the metric system for distances.

Running in 30°C heat, with 70%+ humidity was a shock to the system, but I stuck with it and after a couple of months I felt like running a slightly longer distance in a proper race. I chose the 15km CSC Run by the Bay (more than doubling the distance of my previous 7km morning runs). That 15km run took me around 1 hour 20 minutes to complete. Crossing the finish line felt like an amazing achievement.

Turning Point

This was when I realised that I no longer hated long-distance running and had, oddly, began to enjoy it. I followed up Run by the Bay, with several other races in 2013, ending the year with the Standard Chartered Half-Marathon (2 hours 10 minutes).

I’ve continued running throughout 2014, including finishing my first full marathon (5 hours 19 minutes). I’ve just kept going and going.

On Reflection

I still wonder what changed to make me enjoy running as much as I do now.

I like running in hot weather and Singapore has that in abundance. I felt miserable running on cold, rainy, windy days in the UK. It was easier to just stay in bed and wait for a sunny day.

Singapore also has a vibrant running culture: there’s a well organised, easy-to-reach running event almost every other week to participate in 1 . This contrasts to running events in Glasgow, where there were only two or three big events per year.

I also find running to be quite peaceful. It’s one of those times when you can’t read emails, respond to text messages, get pestered on Twitter, harassed on Facebook, or get bogged down by some coding issue. I just put my headphones in and go.

Some Statistics

From April 2013 - August 2014:

New Pairs of Trainers: 3
Running Distance: ~780km 2
Races Completed: 7

  1. I don't take part in every race. I'd be bankrupt.

  2. Stats from tracked runs using Runkeeper. Some runs are unaccounted for.

Google Authorship Has Been Removed From Search Results

I read about profile pictures being dropped from search results back in June. However, as reported by The Next Web, authorship is now being completely removed.

"Now that authorship has been dropped completely, webmasters can remove all rel=author markup they have on their site. All the effort adding and maintaining the functionality has now gone out the window, but that’s what happens when so many Web properties rely on a single source for driving massive traffic."

It's hard to tell what the usage statistics are for Google+ due to Google forcibly integrating accounts across products (e.g. YouTube to Google+). Thus, we'll never how many accounts would have been setup by choice.

But it leads me to ask what's left for Google+? What niche does it fill? The only reason I set it up was for authorship and therefore it's now of no use to me. Twitter and Facebook continue to fill every other need I have from a social network perspective.

Goodbye Google+.

I'm off to remove my authorship tags and profile.

Digital Ocean Referral

I've been using Digital Ocean for a few months now and their service is fantastic.

From testing Wordpress sites to trying Ghost to hosting the fonts for this website, I've been very impressed with both the simplicity and cost of running servers with them.

If you sign up using this link you'll receive $10.00 in credits. The lowest price tier is just $5.00/month, so that's at best a two-month free trial.

Swift - Private Selectors

Using notification center with Swift is simple:

self.notification.addObserver(self, selector: "notificationReceived:", name: GKPlayerAuthenticationDidChangeNotificationName, object: nil)

However, if you declare your selector's function as private, you'll get a crash when the selector is called (unknown selector sent to instance):

private func notificationReceived(notification: NSNotification)
        // Do stuff.

The key to solving is this snippet from the Using Swift with Cocoa and Objective-C Guide:

"The compiler does not automatically insert the @objc attribute for declarations marked with the private access-level modifier."

The fix is simple:

@objc private func notificationReceived(notification: NSNotification)
        // Do stuff.

No crash and the private access level is maintained.

Free Trials Available for In-App Subscriptions

A new announcement on iTunes Connect:

"You can now offer a free trial for all new In-App Purchase subscriptions. If you offer a free trial, it will begin when a customer subscribes. They will be automatically charged when the free trial is over, unless they have turned off automatic renewal."

I need to understand how this differs from the free trial offered by basically every publication in Newsstand.

Domain Transfer Hell - Resolved

Transfer 1

A few weeks ago I wrote about how I was going through hell trying to transfer a domain from one provider to another.

I had one old domain (www.stuarticus.com) that was with QOXY.com, which I had just renewed and wanted to transfer so I could manage the DNS.1 However, I ran into issues: QOXY had just switched registrars and that meant I couldn’t transfer domains for a period of 60 days. No matter how hard I tried, I couldn’t convince QOXY that this was an issue they needed to help resolve, despite days of back-and-forth emails.

I decided to take it up with the compliance team at the registrar (Public Domain Registry) themselves. Within 24 hours of emailing them and explaining the situation, they’d resolved it for me.

It took approximately nine days to move from QOXY to Hover.

Transfer 2

I wanted to manage all my domains in one place, so I decided to move my main domain (www.stuarticus.net) from IWantMyName2 to Hover (after the previous ordeal). I started the process at 13:42, and by 14:17 on the same day, it was transferred.

This is how it should be done.

The difference in the quality of customer service was simply astounding. I’ve heard many people say that registrar providers can be awful, but I’d never experienced it until now.

In short, I’d recommend IWantMyName and Hover. I’d avoid QOXY.

  1. QOXY don’t provide DNS management unless you have a hosting package. While they can package their products as they see fit, restricting DNS management to hosting packages is hugely inconvenient.

  2. I have nothing against IWantMyName. In fact, I’ve registered many domains with them and they’ve always been very helpful and efficient.

Microsoft's New IE Support Policy

From the MSDN blog:

"After January 12, 2016, only the most recent version of Internet Explorer available for a supported operating system will receive technical support and security updates. For example, customers using Internet Explorer 8, Internet Explorer 9, or Internet Explorer 10 on Windows 7 SP1 should migrate to Internet Explorer 11 to continue receiving security updates and technical support."

For enterprises supporting legacy applications, this will be huge.

Examining the new UISearchController API

I've been examing the new UISearchController API which replaces the UISearchDisplayController API that has been around since iOS 3. My immediate impression is that it drastically simplifies the task of managing a search bar and displaying search results.

You can have a UISearchController up and running with very little code. In the sample project I've put on GitHub which lists and searches through an array of countries, I've configured a UISearchController with the following code in my viewController viewDidLoad method:

self.countrySearchController = ({
            let controller = UISearchController(searchResultsController: nil)
            controller.searchResultsUpdater = self
            controller.searchBar.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(self.countryTable.frame), 44.0)
            self.countryTable.tableHeaderView = controller.searchBar

            return controller

There are a few items to point out in this code:

  • nil is passed in the UISearchController initialiser so as to ensure that the results of the search are presented in the current viewController.
  • The frame of the searchBar must be set, otherwise it has a height of 0.
  • As per the above, the UISearchControllers searchBar is added to the tableHeaderView. There is no need to add a UISearchBar or UISearchDisplayController in Interface Builder.

The viewController must conform to UISearchResultsUpdating protocol, which contains a single method:

func updateSearchResultsForSearchController(searchController: UISearchController!)

When that method is called, search results are updated based on what String is held the searchController.searchBar.text variable.

It's a remarkably simple API to use.

Ultra Light View Controllers with Swift Extensions

One of the big gripes about using the Model-View-Controller paradigm was the resultant massive view controller you’d end up with. I always had large viewControllers when tableViews were involved. In Objective-C, I started using separate tableView managers that acted as the data ource and delegate. It would usually look something like:

TableViewManager *manager = [TableViewManager new];
self.tableView.delegate = manager;
self.tableView.dataSource = manager;

While this made for a lighter viewController, I still felt as though I was tying everything together with string.

Swift has, joyously, introduced Extensions1. These make me very happy. As per Apple’s Swift Language Guide, Extensions add new functionalities to an existing class, notably allowing an existing type to conform to a protocol.

The beauty is that you don’t need to declare your extensions in the same file. If you have a look at the example viewController in my sample project, ViewController.swift, it’s only 36 lines long (including comments). There is no need to declare conformance to (or implement) protocols in the viewController.

Instead, what I’m doing is creating a separate extension file when needed. In this case, ViewControllerExtensions.swift, where I can declare conformance to various protocols as required.

You can declare as many extensions as you want in a single file and the syntax is straightforward:

extension ViewController: UITableViewDelegate
    func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)

The sample project with this approach to extensions is available on GitHub.

Note: all of this is possible with Objective-C Categories.

  1. Not to be confused with iOS 8 App Extensions

Premier League warns about posting goal videos online

Dan Johnson, Head of Communications at the Premier League:

"It's a breach of copyright and we would discourage fans from doing it, we're developing technologies like gif crawlers, Vine crawlers, working with Twitter to look to curtail this kind of activity."

It'll be fun to see how this is enforced and the subsequent reaction.

P.S. I really like the phrase "gif crawler".