iOS

The API

The Short Story

The short story is that PointsStalker can now update the Athletes and Points database on its own. PointsStalker checks for new databases at startup and will download the updated data in the background. The data associated with Groups will be kept, so updates should be unnoticeable aside from new data being presented when viewing an athlete. 

The Long Story 

Building the API server was a longer path of discovery than I expected. This was my first time building any kind of web service and I learned a lot. 

The API Basics

I decided to build the API on Sinatra. I liked Sinatra because it felt straight forward, all the routes are defined in one file, but still offers a lot of flexibility because it is built on top of Rack. Everything in one file; sounds simple right? Well, it is, but considering this was my first project in Ruby I had some catching up to do.

I felt it went well though, I did a few Ruby, Sinatra and Ruby on Rails tutorials and I jumped in. The basics came pretty easily, but, as with everything, the devil is in the details (gem file conflicts, configuring middleware, deploying to Heroku, etc.).

How the API Works  

Since it's my first time writing both iOS apps and server APIs I wanted to keep each component as basic and modular as possible. In particular, I figured I could always add functionality or complexity in the future, but I wanted to avoid getting stuck trying to do too much (or as my Dad used to say, 'come in hot and run out of talent'). So, the API is designed to deliver a JSON hash of the most recent database information the API is aware of for that version of the iOS app. Such data includes the list ID number, the date the list is valid, the date the list is valid to and, most importantly, a download link for this most recent database.

Upon receiving the latest database information for the API the iOS app will compare the JSON information to the information currently held on the phone and if the database held by the API is more recent then the iOS app will download the new SQLite database to replace the out of date information. 

Connecting to the API

When I started working on PointsStalker I had plans for how to approach every component of the app — including backup plans incase I got in over my head — every component except for one, networking. I knew almost nothing about networking from a programming perspective, yet it was critical to PointsStalker's overall functionality. So, in the process of reading the thousands of StackOverflow posts and listening to hundreds of developer podcasts while developing PointsStalker I always kept networking in the back of my mind. And at some point along the way I heard Marco Arment talking about using AFNetworking, so I kept it in mind for later. 

As it turns out, I probably shouldn't have been so concerned about networking. The internet is part of the foundation of nearly every single app created today, thus the tools must to be well developed and easily understood. It was incredibly simple to set up AFNetworking, make my HTTP GET requests and parse the JSON responses. It was even easier to download a link to a file in my S3 bucket, but my bubble had to burst at some point.

Configuring Background Fetch and Background Downloads

I fell back to earth while trying to configure the iOS background fetch API. The fetch API wasn't an issue, just figure out how to use the completion handler and implement application:performFetchWithCompletionHandler: in the app delegate. The problem became handling background downloads.

Again, the iOS API for handling background NSURLSessions was easy enough to understand. My difficulties stemmed from how to configure AFNetworking to use a NSURLSessionConfiguration for background downloading (which seemed more difficult than I felt it would to be). Eventually I got it figured out, but either I missed something or AFNetworking doesn't have a convenient way to configure download request for background downloading using the standard download methods. If you're in search of a solution check out this StackOverflow answer

The Result

With the API server up and running and the app successfully functioning with background API fetching and background downloads I feel good. There are still some bugs I need to try to iron out, but I'd feel pretty good about shipping what I have today. 

Next

In Progress: 

  • UI Tweaks

Bugs Identified:

  • DOB 1 day off for some people
  • Database status string doesn't update
  • Points graph legend gets compressed when using 'cursor'

The week of 4-7-2014

The Idea

I'm staring to think I need to post about what I'm working on both to inform users about the work I do, but also to keep a record for my own review in the future. So, seeing as this is my first post I'm not sure what future posts will include, but I figure weekly-ish log of my activity could be helpful. 

So Far...

The Database

The foundation of the app (at least in it's current incarnation) is the database that will back the athlete table-view and the athlete detail-view. I considered building the database using sqlite and the Gus Mueller's highly recommended FMDB and Marco Arment's accompanying FCModel, but after considering my dearth of SQL and iOS knowledge I thought it best to let Apple's documentation and templates lead the way. So, I decided to use Apple's Core Data to build the athlete and points database. 

Inside the Core Data model I have two entities, Athlete and PointsList, with a to-many relationship between Athletes and PointsLists. The attributes associated with each entity are meant to mirror the columns included in the downloadable .csv files published by FIS.  For the time being I have elected to keep all the available information the CoreData database for completeness, plus I'm not currently concerned about the size of the database since additions are relatively infrequent. If size does become an issue I can always migrate to a new database that excludes less-important information such as an athlete's National ID, Club and World Rankings, but for the time being I think those will be useful. 

I haven't completely decided how I want to get the updated FIS data into the app, but following the objc.io suggestions on Importing Large Data Sets I currently plan on including a seed database in the app bundle and updating it via a web server. To create the Core Data seed database I wrote a command-line tool, similar to objc.io's Core Data importer, that reads from the various FIS .csv files to create the Athletes and PointsList SQLite databases using the app's Core Data model. This import utility should also be useful for creating updated SQLite databases on the web server. 

What I haven't decided on yet is how I want to store user generated collections of athletes. At first, I thought I would just add a UserList entity with a to-many relationship for Athletes, but if I plan on replacing that entire database for updates I need to come up with an alternative method. I'm currently thinking that a .plist that holds the names of lists and then the FIS IDs of the athletes included each list should do the trick. An alternative method would be to setup a second Core Data stack for holding just user generated list data. 

The App

The basics of the app seem solid. I started by reading up on Core Data using Tim Isted and Tom Harrington's Core Data for iOS and then built on top of Apple's iOS template 'Master-Detail Application' and so far so good. The template came preconfigured with a table-view and a detail-view that cover the basics of what I need for views and implementing search with fetch requests and fetched results controllers was relatively straight forward. 

I've done some minimal customization of the table-view cells and the detail view at this point, but I think that will be a major focus in the upcoming weeks. I want to provide coaches, athletes and parents with an in-depth detail view that shows not only the current details on an athlete, but also a synopsis of their points history with a graph or trend line. 

Today's Challenges

Today I've been looking into two challenges: where to store database state information (I'm thinking a .plist, but I don't currently know anything about them) and how to create plots for the detail view (I'm thinking of using ios-linechart that's part of CocoaPods).