I recently released Extended, a SoundCloud client aimed at handling long tracks such as DJ sets. I built this because the current SoundCloud client does not keep track of positions, causing you to loose your place if the app is killed or you switch tracks.
The precedent
My background has predominantly been web focused, during university I helped build internal apps for a energy/rail company before a brief stint at Rummble working on a simple crowd sourcing recommendations tool.. In my last year I flirted with iOS development helping build an application for our state ambulance service before moving internally to extending a ASP.NET MVC application.
After university I joined a risk company helping build out their Rails application to accomodate client specific needs (evening experiencing the joy of travelling to beautiful Switzerland) before landing at what has been my most joyous employment to date: Discovr. Discovr was excellent team blend, combining powerful backend infrastructure with an exciting iOS application and promising web frontend. Sadly Discovr hit some issues and the company was wound down, but it and Darcy, Stu, Dave, Chris, Ben, Matt & Matt taught me a lot about application scaling, API design, how startups should work and iOS development.
The itch
I’ve recently started to going to the gym more frequently and I’ve been enjoying extended sets from the likes of Above & Beyond, Eric Prydz and Thomas Gold but I found the existing SoundCloud unable to keep track of positions during application kills or track switching. This led me to believe I could potentially write my own app for iOS in Obj-C.
Objective-C?
If you like Ruby you might be tempted to write your application in Rubymotion. I’d recommended against it as this is your chance to get as close as possible with the device and platform you’re building for. Learning Objective-C will make you aware of the limitations of a mobile device that rightfully lacks a garbage collector and understand Apple’s SDK better.
Once you’re aware of Objective-C then feel free to switch on to RubyMotion, but it’s at least wise to understand what you’re building on. It’s good to experience the world outside of Ruby and deal with statically typed languages.
Embrace Objective-C’s verbosity, it’s an excellent way to communicate publically what a method call does. It makes more sense when you have multiple methods that expand on each other to provide additional context.
The initial setup
You’ll want to check out Cocoapods if you plan on using anything other than the native Apple libraries, it’s like bundler but for Xcode projects. It’s available as a gem and will walk you through the process of setting up compile references.
I’d really recommend paying the iOS development subscription to Apple, the iOS simulator is fast but there’s nothing like feeling the interaction between your finger and the code. You should be able to use the Organizer (Window > Organizer) to ensure your device is prepped for testing.
Skinny controllers
As for laying out your application I’d recommend the skinny controllers approach that is quite popular in modern Rails development. Your view controllers should deal with handling view rendering and view/event interaction while forwarding anything complicated to a manager.
For example: LibraryViewController in Extended asks LibraryManager for an array of the top 100 SoundCloudLibraryItems and then deals with rendering them in a tableview. It does not deal with any database queries, that is the manager’s job. The library manager also concerns itself with pulling changes from the SoundCloud API, it sends out a NSNotification via NSNotificationCenter when any data is changed and the ViewControllers subscribe on load and respond with refreshing their caches and the tableView.
The same goes for SoundCloudAudioPlayer: the LibraryViewController receives a tap event, it looks at its local cache of items and notifies LibraryManager that it wishes to play that item, the manager then calls the audio player.
Skinny controllers goes a long way to making your code reusable, testable and ultimately better to share amongst a team.
UI
In the future I’m going to really focus more on developing interfaces with code and less with Xcode’s storyboard and autolayout functionality. As a developer I’m interested in debugging the decisions that made my UI elements shift (watch out for 3.5inch devices and the call status bar) and sadly storyboard and the like hide that from me. With UIs developed in code you can instantly see where your positioning is being set and any changes you may need to combat.
Storyboarding still feels quite buggy during development in Xcode, I found cases where I’d scroll to the bottom right and then be kicked back to the top left.
This is a must read on choosing between when to code and when to IB.
When it comes to rendering items such as a view or UITableViewCell you want to be as quick as possible, don’t go to the database to fetch data instead try to have the previous view keep a cache of the data and then pass the objects onto your new view. For UITableViewControllers I have the initial load of a table fetch a cache from the Manager and then subsequent calls for each cell just hit the local cache.
If you’re terrible with simple icons like a heart or a audio player icon then you’ll want to grab some a glyph set and import them using this guide.
Don’t be a jerk
You’re building an app that runs on a incredibly personal device, you should feel privileged to be consuming the user’s time.
Assume and own resources when you need them on-demand. Don’t grab everything or request permission for everything when the application loads, you want the user to boot up your application with a smooth transition that does not feel foreign.
If you want to begin ownership of the Audio system for say playback, begin owning on the first playback and not when your app launches. A lot of applications own it at first launch, causing anything the user is currently playing to be killed! That’s one sure way to piss off a user.
Same goes for permissions, only ask for push notifications or location updates when you need it. Imagine being bombarded with UIAlerts when you first launch, it’s incredibly overwhelming.
Singletons
Because I really only wanted one SoundCloudAudioPlayer for the lifecycle of the application I made use of the singleton pattern for these types of classes (Managers as well). This allowed view controllers to not worry about using say the AppDelegate for coordination of the “current” player, they just had to ask the class itself for what is the current instance.
Delegates
If you’re following the skinny controller method you might want one instance to ask another instance questions, for instance SoundCloudAudioPlayer will ask the relevant LibraryManager what the next track is coming up.
To achieve this you can setup a class that is told about a delegate. This delegate promises to conform to a contract, a select implementation of certain methods. The class told about the delegate only cares about the methods specified in the delegate, so the delegate can implement any other methods or even other contracts. These contracts are called protocols, in summary:
- SoundCloudAudioPlayer has a property
NSObject<PlaylistHinting>* delegate. SoundCloudLibraryManager : NSObject<PlaylistHinting>declares it implements the protoctolPlaylistHinting.PlaylistHintingis dead simple.SoundCloudLibraryManagercan do a lot of other methods outside of delegate stuff.
Delegates are incredibly important when you being making network requests, you create and kick off off a network request and later the network request will call a delegate with the connection result. In this case usualy the delegate will be yourself.
Ruby comforts
Good news, Objective-C has blocks! If you want to enumerate objects you can easily do something like this [someArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { //some code }];.
Objective-C like Ruby operates through sending messages, if you wish to send things to an instance you can get dangerous pretty easily thanks to @selector() or NSSelectorFromString which you can send to an object via [self performSelector:<#(SEL)#>]. Or if you want to get even crazier you can performSelectorInBackground.
If you looking for some comforts around Enumeration this looks pretty promising.
Database
For the ambulance project I while back we were working with CoreData which can best be described as a relational object database. It’s certainly nothing like ActiveRecord and may not be best suited to your application needs, it’s somewhat complicated but has a lot of intelligence under the hood for migrations and relational mapping.
As I wasn’t needing complicated data storage I opted for Marco Arment’s FCModel, it’s a nice library on top of SQLite that does away with worrying about relational mappings or migrations. Instead you are writing classes much like ActiveRecord/Model, creating the migrations yourself and then querying them using plain old SQL. This fits my needs perfectly: I just needed a place to cache tracks from the SoundCloud API without any need for maintaing relational mapping.
You’ll find FCModel has the convenience of ActiveRecord’s callbacks, check out the implementation here to learn what callbacks you can subclass. I found myself subclassing save and create callbacks to correctly set the createdAt and updatedAt timings much like AR does.
If you find you enjoy migrations I’ve started extracting my migration library for FCModel in which you specify your migrations much like Rails migrations are performed, in code.
So for some FCModel might not do enough, but if you’re interested in just keeping local copies of data and enjoy the freedom of controlling your own SQL queries then give FCModel a try.
Backend (server) components
Extended has a basic Explore tab that allows people to view tracks that I’ve curated from SoundCloud, it’s good for those who don’t have a SoundCloud account or want to justify the price of a 99cent app.
Extended has no public endpoint and no administration panel.
Discovr had a huge infrastructure behind the client application, it had a large set of workers bringing in external data, a robust and extensive API for client operations and growing admin panel for data corrections. I wanted to avoid the hassles of scaling out my backend if Extended some how became popular.
So I’ve turned to AWS S3. Usually in most apps clients would hit a Rails application for a delta update since a specific date point, leaving the database to be optimised around last updated/inserted. While ExtendedData also maintains a database to record which tracks I’ve selected for curation it regularly publishes a diff to an S3 bucket for the clients to consume. The flow looks something like this
- A Google spreadsheet is looked at for the curated SoundCloud accounts to check
- An import process looks for any new tracks published by those selected number of SoundCloud accounts
- An second import process looks at my own personal sets for new playlists to add
- The export process looks at
latest.jsonin S3 for the last time the export was published grabs the tracks newer than that date. If there is nothing new, the process quits. - Any new items are published into a new
latest.jsonand the oldlatest.jsonis moved to the date it was published (eg1395061443.json), withlatest.jsoncontaining a key that points to the previous latest.
The clients then hit latest.json and following the linked JSON files for up to a depth of 8. If at anytime they see a file that they have processed before they bail out and record in a simple KeyValueStore model what the most recent processing was.
You could say latest.json behaves like a linked list.
Test
Test on both the simulator (in both sizes) and your device. Assume the simulator is bug free and your code is wrong until you can prove otherwise, I made the mistake of casting a 64bit integer badly causing it to work fine on my iPhone 5S and then see weird time rendering on the simulator. It wasn’t until I tested on a non 64bit ARM device that I found out I had screwed up and had been stupidly thinking the simulator is buggy all the time.
- Test with the in-call status bar. Try switching it on during the application and try having it on before the application launches and then see how your UI responds when it goes away. You can do this on the simulator otherwise you can try tethering.
- If you’re doing anything network, try your app with just WiFi and then try switching off the WiFi and leaving no internet available. I’m using NSNotifications sent to the
TabBarViewControllerto rendering a error notification that scrolls in from the top. I’ve added Reachability for knowing when the network returns. - Turn to Testflight for distributing test builds, you’ll want to read this document on how to setup Ad-Hoc profiles (hint: you can’t use Xcode Organiser well).
- Use Safari for the iOS Dev portal, it sometimes loads in-secure resources that cause Chrome to kill requests such as adding a device (it still adds it but the view loading hangs).
- Setup Bugsense or Crashlytics for tracking your crashes, in the case of Crashlytics make sure you sign up well ahead of time as they operate on an invite only basis.
Be rough with your app: open and close views and watch how well your app manages resources. If you see ballooning between view loading with not much reduction after a period then step through init code and see if you need those resources.
As for automated tests, I’ve been using XCTest for various parts but I can’t comment much on it at this stage. SlimMigration makes use of XCTests here.
App Store
I found the App Store submission process great, just be prepared for a lot of uploading as the screenshot uploader lacks a multi uploader.
Assume that the reviewer has no way of creating any external accounts, I found my app rejected because I failed to provide them with a SoundCloud account. After correcting that and resubmitting the new metadata it was less than 23 hours before I was in the store.



