An Adventure in Arrays with Apple Swift

| | 0 Comments| 7:01 AM
Categories:

No, this is not another blog post restating the obvious or reiterating what you could have read in Apple’s Swift language reference. Our goal here is to show you how to regain the lost mojo of the lastObject method of NSArray.

For the most part arrays in Swift are mundane and appear syntactically similar to array instantiation and usage in many other languages. A bit of Python:

[python]
shoppingList = [‘bananas’, ‘milk’, ‘vanilla ice cream’]
itemCount = len(shoppingList)
print "There are %d items." % itemCount
for item in shoppingList:
print "I need to buy %s." % item
print "The most important item on my list is definitely %s." % shoppingList[-1]
[/python]

How about some Ruby:

[ruby]
shoppingList = [‘bananas’, ‘milk’, ‘vanilla ice cream’]
itemCount = shoppingList.count
puts "There are #{itemCount} items."
shoppingList.each do |item|
puts "I need to buy #{item}"
end
puts "The most important item on my list is definitely #{shoppingList[-1]}."
[/ruby]

In Objective-C we would use an NSArray of NSString objects, and the most important item on our list could be retrieved with lastObject:

[objc]
NSArray* shoppingList = @[@"bananas", @"milk", @"vanilla ice cream"];
NSLog(@"The most important thing on my list is definitely %@.", [shoppingList lastObject]);
[/objc]

Let’s try that in Swift with:

[objc]
var shoppingList = ["bananas", "milk", "vanilla ice cream"]
var itemCount = shoppingList.count
println("There are \(itemCount) items.")
for item in shoppingList {
println("I need to buy \(item).")
}
println("The most important item on my list is definitely \(shoppingList.lastObject)")
[/objc]

Before we get to the fact that lastObject doesn’t work, notice again how scripty Swift feels. We suppose there are only so many syntactical constructs for creating arrays . We’re glad Swift doesn’t borrow too much from Erlang.

But we digress. We assume Swift, like Objective-C, will give us a lastObject property or method on our array. Oh, no, it doesn’t. Playground execution failed: error: :18:61: error: 'Array' does not have a member named 'lastObject'
println("The most important item on my list is definitely \(shoppingList.lastObject)")
. Let’s try the array[-1] notation that’s popular in Python and Ruby (and I’m sure others) for obtaining the last item in an array:

[objc]
println("The most important item on my list is definitely \(shoppingList[-1])")
[/objc]

The horror! fatal error: Negative Array index is out of range
Playground execution failed: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
Of course we know that Swift arrays are index-based, and that the first index is 0, and the last index of the array is count - 1. So perhaps we can do that:

[objc]
println("The most important item on my list is definitely \(shoppingList[shoppingList.count-1]).")
[/objc]

We can do that, but how awful looking. Yuck, yuck, yuck. If it feels wrong chances are good that it is, and that just doesn’t look right. Oddly enough, Apple does provide a removeLast method on the array, but it does indeed remove the last element (sort of like a stack pop function):

[objc]
var lastItem = shoppingList.removeLast()
println("The most important item on my list is definitely \(lastItem), and now there are \(shoppingList.count) items left.")
[/objc]

results in “The most important item on my list is definitely vanilla ice cream, and now there are 2 items left.”

So, what if we truly do want to access but not remove the last item on the list? Here’s a nice gem we found on StackOverflow that provides this functionality by extending the Array type. Extensions are the Swift equivalent of Objective-C categories that are useful for “adding on” new functionality to an existing class. Here’s the codez (we took a the self.endIndex and replaced with simply endIndex at the suggestion of another reader):

[objc]
extension Array {
var lastObject: T {
return self[endIndex – 1]
}
}
[/objc]

Let’s try it in the playground:

[objc]
println("The most important item on my list is definitely \(shoppingList.lastObject).")
[/objc]

now results in what we expect, and that’s the most important item on my list is definitely vanilla ice cream. What can we say, we love banana milk shakes.

Leave a Reply

Your email address will not be published. Required fields are marked *