As promised, a post on extensions in Swift.
This isn’t going to be an in-depth tutorial. Extensions have been covered better elsewhere, most notably in Apple’s excellent documentation. I just want to show a couple extensions I’ve written: Array#find
, and comparison operators for NSDate
.
Array#find
If you want to find a value in a Swift Array, you can use the global generic find
function:
let arr = [4,8,15,16,23,42]
let idx = find(arr, 15) // Some(2)
However, find
only lets you search for a static value, and what I really wanted was the ability to find the first value that satisfied a test function. For example, say I want to find the first odd number in an array. Let’s extend Array so we can do that.
extension Array {
func find(test: (T) -> Bool) -> Int? {
for i in 0..<self.count {
if test(self[i]) {
return i
}
}
return nil
}
}
let arr = [4,8,15,16,23,42]
let idx = arr.find { $0 % 2 == 1 } // Some(2)
The extension defines a method, find
, on Array, that takes as an argument a function mapping an element of type T to a Bool, and returns an optional Int. In other words, we pass in a test function, and it returns the index of the first value in the array for which that function is true. If no such values are found, it returns nil.
But wait! you say. Why write an extension when you can just add a new signature to the global find
function so that it will take a test function rather than a value? FINE. Be that way.
You can, um, find the method signature of find
in Apple’s documentation:
func find<C: CollectionType where C.Generator.Element: Equatable>(domain: C, value: C.Generator.Element) -> C.Index?
That looks a bit complicated, and CollectionTypes and Generators are a topic for another day, but all you really need to know is that in order to conform to the CollectionType protocol, you need to define a startIndex
and an endIndex
, and be subscriptable. C.Generator.Element is the type of items in the collection. So instead of passing in a value of type C.Generator.Element to find, we want to pass in a test function that takes a C.Generator.Element and returns a Bool. The rest looks very much like our Array extension:
func find<C: CollectionType where C.Generator.Element: Equatable>(domain: C, test: (C.Generator.Element) -> Bool) -> C.Index? {
for i in domain.startIndex..<domain.endIndex {
if test(domain[i]) {
return i
}
}
return nil
}
let arr = [4,8,15,16,23,42]
let idx = find(arr, { $0 % 2 == 1 }) // Some(2)
Comparison Operators for NSDate
NSDate annoyingly does not implement the Comparable protocol, meaning that instead of:
if someDate <= anotherDate { ...
You have to do:
if someDate.compare(anotherDate) == NSComparisonResult.OrderedAscending || someDate.isEqualToDate(anotherDate) { ...
Let’s fix that. According to Apple’s docs, we only need to implement <
and ==
to conform to the Comparable
protocol. Pretty clever, since with just those two, you can derive <=
, >
, >=
, and !=
.
extension NSDate : Comparable {}
public func < (lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.compare(rhs) == NSComparisonResult.OrderedAscending &&
!lhs.isEqualToDate(rhs)
}
public func == (lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.isEqualToDate(rhs)
}
The first line just says that we are extending NSDate
to conform to the Comparable
protocol. You can leave this out, but then you’ll have to implement all the other comparison operators.
The rest is pretty straight-forward. The only curious bit is why the operators are implemented in the global scope, rather than within the extension. I.e. you’d think you could do this:
// This does not work!
extension NSDdate : Comparable {
func < (other:NSDate) -> Bool {
return self.compare(other) == ...
So why not? Operators aren’t methods in Swift, but global functions. They have precedence over methods, and have varying rules around associativity. You can even make up your own operators if you want. Be sure to read NSHipster’s great article on Swift operators.
Swift is a great language, and far more enjoyable to program in than Objective-C, but it is still in its infancy, and bugs abound. One of the more frustrating is compiler errors that are flat-out wrong. Take a look at this (in Xcode 6.1):
“Could not find member ‘date’?” Trust me when I tell you that date
is indeed a member of a Todo
object. So what’s going on? Let’s tweak it a bit:
Ah, there we go. Turns out you can’t actually compare dates like that (hmm…I smell a follow-up post on writing extensions). Here’s how you do it:
Here’s another one. The following is essentially a no-op:
for i in 0..<0 {
println(i)
}
That’s just a for loop from zero up to, but not including, zero. And this code is fine, doing what it is supposed to do—nothing.
However, try making the ending value of the loop less than the starting value. This should be equivalent to the above:
for i in 0...(-1) {
println(i)
}
The ...
operator means “up to and including”. So this should also be a no-op (and it is in most programming languages). But:
Even worse, it will actually crash on the first executable line of code:
None of this should scare you away from using Swift. It’s a fun language to work with, and ends up being much more concise than Objective-C. Just keep in mind that there are still bugs to be worked out, and if you’re getting errors that just make no sense, there’s a fair chance you’ve hit one.
One of the defining charactaristics of a successful programmer is being OK with the feeling that you have no idea what you’re doing. I’ve been programming for over 30 years and it still happens to me regularly. The key is pushing on through that dark period, trusting that eventually you’ll catch a glimmer of light and it will slowly start to make sense.
There are a few tricks I’ve picked up to help me get over that initial hump. The first is to find a tutorial someone has written, and just follow along, just to get something working. Once I have something working, no matter how basic, not only do I start to get a better sense of how all the pieces fit together, but I can start tweaking it and learn from how it responds.
Can’t find a good tutorial? Great! Here’s your opportunity to give back, and write your own. There’s really no better way to learn. Start with doing the most basic thing imaginable. Just get something working. Write down your steps as you go. Then add to it, write it down, repeat. You’ll eventually have something coherent that you can post, and you’ll earn a ton of internet karma points.
It also really helps if you have a specific project to work on, otherwise the tutorial can be a bit of a dead end, and your effort can feel purposeless. Don’t have a project, and can’t think of one? Rewrite something you’ve already written. Ask friends and family if there’s an app they wish they had. Write a basic version of an app that you’re too cheap to buy. Write a todo app.
Start small, though; the bigger the project, the less likely you are to finish it. Minimum viable product (MVP) isn’t just Silicon Valley jargon; it applies equally well to personal projects. Do the simplest, dumbest thing possible. It’s easy to iterate and improve once you have something working, and you can always throw it all away and rewrite it once you feel you know what you’re doing.
The feeling of sucking at something and being completely lost is a part of learning. Everyone starts out that way. If you’re actively avoiding sucking, you’re not learning. Get lost. Seriously, find someplace to get yourself lost. You’ll be better for it. Then tell us about it when you inevitably emerge from the darkness.
Prose.io is a content editor for GitHub. It supports Jekyll sites (like this one), and provides Markdown previews. I’m writing this post using it.
I’m sure I looked at Prose some time ago, but must have decided something was lacking. But I just took a look at it again, and it’s just what I need. In fact, I had started a similar project myself not long ago, and now I feel relieved that I can officially call it dead and stop fretting about having stalled out on it.
The most dangerous thought that you can have as a creative person is to think that you know what you’re doing. Because once you think you know what you’re doing, you stop looking around for other ways of doing things.
— Brett Victor, The Future of Programming
He’s posted references and follow-up notes at http://worrydream.com/dbx/.