First Impressions of Swift

I’ve been playing with Swift, Apple’s replacement for Objective-C, for a couple of weeks now. I’ve been writing Objective-C with Cocoa since around 2002 (I still have the first edition of O’reilly’s Learning Cocoa, published way back in May 2001) and have seen a lot of changes. From manual retain/release to garbage collection to automatic reference counting; the introduction of the UI prefix for iOS classes; from Project Builder and Interface Builder to the unified XCode; xibs have replaced nibs… and now there’s an entirely new language.

Swift has been around in beta for a few months now (and officially released as part of iOS 8) and there have been many articles written about why it was introduced and architecturally what changes it brings, but I’d like to go through some of my thoughts, and take the time to write some of my impressions about some of the interesting things I’ve come across so far. And maybe reminisce about Objective-C just a little bit.

The Learning Curve

The first thing developers notice about Objective-C is its, shall we say, “interesting”, syntax. Let’s compare how to define and instantiate a class in three languages used for relatively different purposes; note that all theses example are done in a very simple way, for example, I’m not retrieving the sum with a computed property. First let’s get enterprisey with C++:

// class member definition
class Adder {
        int number1, number2;
    public:
        Adder (int, int);
        int sum () {return number1 + number;}
};

// constructer definition
Adder::Adder(int a, int b) {
    number1 = a;
    number2 = b;
}

//instantiate and use
Adder myAdder(2, 3);
cout << myAdder.sum() << endl;
Next, some Python simplicity:
# class definition
class Adder(object):
    def __init__(self, number1, number2):  # constructer definition
        self.number1, self.number2 = number1, number2

    def sum(self):
        return self.number1 + self.number2

# instantiate and use
my_adder = Adder(2, 3)
print my_adder.sum()

Finally, some PHP:

// class definition
class Adder {
    function __construct($number1, $number2) {
        $this->number1 = $number1;
        $this->number2 = $number2;
    }

    function sum() {
        return $this->number1 + $this->number1;
    }
}

// instantiate and use
$myAdder = Adder(2, 3);
echo $myAdder->sum() . "\n";

Despite all these languages being for different purposes, they all follow somewhat the same syntax, particularly in regards to their use of brackets to call methods. Now, Objective-C:

// class interface definition
@interface Adder : NSObject

@property (nonatomic, assign) NSInteger number1;
@property (nonatomic, assign) NSInteger number2;

- (id) initWithNumber1:(NSInteger)number1 number2:(NSInteger)number2;
- (NSInteger) sum;
@end

// class implementation
@implementation Adder

// automatically create property methods
@synthesize number1 = _number1;
@synthesize number2 = _number2;

- (id) initWithNumber1:(NSInteger)number1 number2:(NSInteger)number2 {
    if(![super init])
        return nil;

    self.number1 = number1;
    self.number2 = number2;

    return self;
}

- (NSInteger) sum {
    return self.number1 + self.number2;
}

@end

// instantiate and use
Adder *myAdder = [[Adder alloc] initWithNumber1:2 number2:3];
NSLog(@"%d", [myAdder sum]);

So for anyone coming from other languages, Objective-C is a bit weird. I really liked the syntax though: the named method arguments made reading through code really easy, and they way that square brackets always encapsulate a value just seemed to make sense. Swift does a great job of combining the Objective-C way with a traditional syntax, in a manner that should make sense to anyone familiar with either.

// class definition
class Adder: NSObject {
    var number1:Int
    var number2:Int

    init(number1:Int, number2:Int) {
        self.number1 = number1
        self.number2 = number2
        super.init()
    }

    func sum() -> Int {
        return self.number1 + self.number2
    }
}

// instantiate and use
var myAdder : Adder = Adder(number1:2, number2:3)
println(myAdder.sum())

Swift keeps the named parameters that are oh-so-handy, but its syntax is much more “C-Like” (for lack a of a better term) so it seems familiar no matter what your coding background is.

Integration with Objective-C

The introduction of the iOS App Store saw an explosion in popularity of Objective-C, and nowadays if you want a library that does… something… there’s probably and Objective-C version of it out there. I thought that using Objective-C and Swift together would be a huge pain in the ass, but it’s really simple. XCode will generate for you a bridging header, and in there you just import the headers for any Objective-C classes you want to use in your Swift code. You can even keep using CocoaPods for dependency management. In a project I’m working on, I’m using the AFNetworking library (among other things) so my bridging file looks like this:

//  Contents of Project-Bridging-Header.h

#import "AFNetworking.h"

If you’re a beginner and you’re stuck with something iOS related, you’ll probably find the answer online (StackOverflow!), but more than likely it will be written in Objective-C. Luckily, it’s relatively simple to convert the syntax to Swift. For example, the AFNetworking demo code for getting some JSON. The Objective-C version:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:@"http://example.com/resources.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

And its Swift equivalent looks like this:

var manager:AFHTTPRequestOperationManager = AFHTTPRequestOperationManager()

manager.GET("http://example.com/resources.json", parameters: nil,
    success: { (operation, responseObject) in
        println("JSON: \(responseObject)")
    }, failure: { (operation, error)in
        println("Error: \(error)")

    }
)

So you just change the order of variable definitions (var name:type instead of type *name), get rid of some *s and rework the loggers a little bit, and you’re done.

The biggest change is instead of calling a convenience constructer to create an (autoreleased, in the olden days) version of the object, you “call” the class directly — in this case, you might assume that you’d want to call AFHTTPRequestOperationManager.manager(), but if you do, XCode helpfully tells you to use just AFHTTPRequestOperationManager() instead.

Of course this is a simple example, but I’ve found with more difficult pieces of code, XCode is very good at suggesting where you went wrong, and helpfully fixes syntax errors for you.

Your Code was Bad, and You should feel Bad

One thing that I found when I started writing Swift was just how often XCode will throw up a warning as soon as you do something seemingly-correct, and sometimes these errors seem to be completely unrelated.

For example, say you’re working on a UITableViewController subclass, and you want to add an array to be the data source. So you do this:

class MyTableViewController: UITableViewController {
    var myDataSource:NSArray  // added this instance variable

    override func viewDidLoad() {
        super.viewDidLoad()

        /* snip */
    }

    /* snip */
}

Then two errors pop up straight away:

Class 'MyTableViewController' has no initializers. 'required' initializer 'init(coder:)' must be provided by subclass of 'UITableViewController'.

What? But wait, I didn’t need an init before did I? OK, let’s add one.

class MyTableViewController: UITableViewController {
    var myDataSource:NSArray

    required init(coder aDecoder: NSCoder) {  // add init
        super.init(coder:aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        /* snip */
    }

    /* snip */
}

Sweet, that was easy… wait, more errors?

Property 'self.myDataSource' not initialized at super.init call.

OK, I think I get it now: my instance variable is never initialised. So to fix all the errors:

class MyTableViewController: UITableViewController {
    var myDataSource:NSArray

    required init(coder aDecoder: NSCoder) {
        self.myDataSource = NSArray()  // instantiate instance var

        super.init(coder:aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        /* snip */
    }

    /* snip */
}

Finally, no errors. But it’s a lot of extra code.

What I should have done is just tell XCode that the variable is optional, and I’ll be sure it’s there before I try to use it. Do this by taking the original code, and adding a ? to the end of the instance variable declaration. Swift won’t be so picky about the variable not being set in your constructer, you just have to be certain you’ve set it before you try to use it; it’s OK to set it up in your viewDidLoad function, for example, as any other methods that use the variable won’t be called until after this.

class MyTableViewController: UITableViewController {
    var myDataSource:NSArray?  // add ?

    override func viewDidLoad() {
        super.viewDidLoad()

        /* snip */
    }

    /* snip */
}

Again warnings are silenced… until I try to use the variable.

class MyTableViewController: UITableViewController {
    /* snip */

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.myDataSource.count
    }

    /* snip */
}
'NSArray?' does not have a member named 'count'.

What? I’m pretty sure you can get the count of an NSArray, it’s fundamental. Oh wait, it’s not an NSArray, it’s an NSArray?. So it sometimes it might not be there.

To fix this issue, you need to make a promise to XCode that you know what you’re doing and you swear that when it comes time to use the variable, there will be something there. Do this by adding a !.

class MyTableViewController: UITableViewController {
    /* snip */

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.myDataSource!.count  // add !
    }

    /* snip */
}

Now you’ve promised Swift that even though the variable is optional, it will have been set when it comes to this use, and no more errors exist.

Other Little things

Structs have replaced enums for constants. For example, NSTextAlignmentCenter becomes NSTextAlignment.Center With code completion, you’re not going to really notice much difference.

Oh, and UI_USER_INTERFACE_IDIOM() is replaced by UIDevice.currentDevice().userInterfaceIdiom. And I’m sure there’s tons more changes like this, but that’s what have stuck out so far.

Conclusion

After a couple of weeks with Swift, I’m impressed with how easy it was to transition to from Objective-C, especially with how seamlessly the two integrate together. The strictness of Swift, which at first seems constricting, I’m sure will lead to more predictable programming and more stable applications.

Previous entry

Next entry