Starting to love Swift / by Jake MacMullin

I've been writing a lot of Swift since it was first released. I enjoy learning new things and using new languages so I've mostly enjoyed using Swift. I'm taking a conservative approach to the language though. I know Swift has many features that weren't available to me in Objective-C. I could be making more frequent use of generics, tuples, and using structs and enumerations rather than classes. I could use operator overloading and unicode support to write programs that do clever things with emoji. Rather than rush in to using all of these features of Swift in every line of code, I've largely been writing code in much the same way as I would in Objective-C. That is, I'm developing iOS apps using UIKit, Foundation etc. Most of the time, I'm writing pretty much the same statements with a different (and in my opinion nicer) syntax.

However, slowly I'm starting to really love this language.

Here's one example. I'm sure this is possible in Objective-C, but I'm not sure I would have found as concise a way to express this in Objective-C as I've ended up with in Swift.

I've got a list of items of content in an app I'm working on. For this example, let's call them songs. Each song has a listenCount property which is the number of times it has been listened to and a lastListened property which is the date it was last listened to. The user interface for this app has a special place for the current 4 favourite songs. So I need an algorithm that will take the list of all songs and return the 4 current favourites. I'm defining current favourites as the ones that have been listened to within the last 2 weeks with the highest listenCount.

I started thinking about how to approach this: "I'll need some sort of data structure to hold the four favourites" I thought, declaring an array. "Then I'll need to iterate through my list of songs and consider each song" I thought, writing a for-each loop. "Ok, so for each song I'll need to check to see if its lastListened property is within the past two weeks" I decided, writing a conditional. "If it has been listened to recently, then I need to compare its listenCount to the four highest listenCounts I've encountered so far" I thought, considering another for-each loop to compare the listenCount of the song under consideration with the listenCount of the four songs with the highest listen count so far.

It was at this point that I paused to wonder if there was an easier way to approach this. The thought of having to figure out how to maintain an array of the four most listened to songs (in order) whilst I looped through all the available songs just seemed like too much work. I've been using some of Swift's built-in functions for dealing with arrays and it occurred to me that this could be just the sort of problem they could help with. As soon as I considered the problem in these terms, a solution was obvious.

First, I wanted to filter the array of all songs down to one containing only those songs that had been listened to recently. Swift's filter function is perfect for this. It allows me to express a function that should be used to determine if a given song should be under consideration as a favourite:

var recentlyListenedSongs = songs.filter { $0.lastListened > recentPeriodTimeInterval }

Then it is just a matter of sorting the songs by listenCount and taking the first 4:

recentlyListenedSongs.sort { $0.listenCount > $1.listenCount }
if recentlyListenedSongs.count > 4 {
    recentlyListenedSongs = Array(recentlyListenedSongs[0...3])
}

Simple. So, why didn't I think of this straight away?

I asked some friends how they'd approach this problem in Objective-C and they quickly pointed out filteredArrayUsingPredicate and sortedArrayUsingComparator. Objective-C has the same ability to filter and sort arrays with closures as Swift does. And yet, I don't know if I'd have thought to use them if I was writing this program in Objective-C. It could be that I'm attributing my newfound comfort with manipulating arrays with functions to Swift when its actually something entirely independent of Swift. Or it could be that I wouldn't have considered this approach in Objective-C because although it now supports this approach, it didn't when I first learnt the language (I know, these methods have been around for a while). 

Or it could be that Swift's syntax makes using closures so much nicer. Here's a side-by-side comparison of the same algorithm in Swift and Objective-C:

Swift:

var recentlyListenedSongs = songs.filter { $0.recentlyListened == true }
recentlyListenedSongs.sort { $0.listenCount > $1.listenCount }
if recentlyListenedSongs.count > 4 {
    recentlyListenedSongs = Array(recentlyListenedSongs[0...3])
}

Objective-C:

NSArray *recentlyListenedSongs = [songs filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"recentlyListened == YES"]];
recentlyListenedSongs = [recentlyListenedSongs sortedArrayUsingComparator:^NSComparisonResult(Song *obj1, Song *obj2) {
       return obj1.listenCount < obj2.listenCount;
}];
if (recentlyListenedSongs.count) > 4 {
      recentlyListenedSongs = [recentlyListenedSongs subarrayWithRange:NSMakeRange(0, 4)]
}

So, either Swift has made it easier for me to use functional techniques for manipulating arrays - or its just a coincidence that I'm becoming more comfortable with this approach at the same time as I'm learning Swift - and I'm incorrectly attributing it to Swift. But that's how love works, right? Maybe I'm just so enamoured with Swift I'll give it credit for everything.