October 1, 2015 Swift Generics Practical Examples

With the introduction of Swift last year, it was obvious the trend in iOS programming will have a huge impact. The move from Objective-c, with features like including dynamic typing, method forwarding, method swizzling and of course posing (replacing an entire class implementation) to a static strong type inference programming language. You can even see the tone difference between WWDC14, WWDC15 and previous WWDCs; Going from the benefits and the merits of being dynamic and now the advantages of a strongly typed language.

I see this move is a very good move, and one of the advantages is reducing programmer errors through the use of Swift generics. Have you tried to use generics in your own code? Most developers who write business apps that talk to web services, store data locally, and show data to a user do not likely use generics beyond what the standard library offers. This is not a bad thing. You usually write generics code for a reusable component and then you use it everywhere in your app. Let’s take a look.

Practical Example #1: ArrayDataSource

Perhaps the most obvious place to use generics is data source for a UITableViewDelegate. So, let’s go ahead and create one.

class ArrayDataSource: NSObject, UITableViewDataSource {
    typealias ConfigureBlock = (item: ItemType, cell: CellType, indexPath: NSIndexPath) -> Void
    let cellIdentifier: String
    var configureBlock: ConfigureBlock
    var items: [ItemType]
    init(cellIdentifier: String, items: [ItemType], configureBlock:ConfigureBlock) {
        self.cellIdentifier = cellIdentifier
        self.items = items
        self.configureBlock = configureBlock
    }
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as? CellType else {
            fatalError("Cell with identifier (cellIdentifier) should be of type (CellType.self)")
        }
        configureBlock(item: items[indexPath.row], cell: cell, indexPath: indexPath)
        return cell
    }
}

Please note that the above code was not possible with Swift 1.2. We were not able to create a generic class that inherits from NSObject.

Now, to use this class, you simple create it and assign it as data source of your table view.

class TableViewController: UITableViewController {
    let dataSource = ArrayDataSource(cellIdentifier: "cell", items: ["Cell 1", "Cell 2"]) { (item, cell, indexPath) -> Void in
        cell.textLabel?.text = item
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = dataSource
    }
}

As you can see we created a reusable class that almost all iOS developers might have a need to use. Now, if you changed your model class, the compiler will give you errors instead of your manager telling you the app crashes. I know it’s very simplified version of a data source and I’m in the process of writing a more complex component for handling data source in a generic way.

Practical Example #2: KVOController

Let’s look at another area where generics can be used. One of the worst APIs in cocoa and cocoa touch is key value observing (KVO). In order to use it, you have to do several things

  • -Register for property as an observer using addObserver
  • -Implement observeValueForKeyPath and
  • -Provide if/else statements for different key paths and contexts and
  • -finally unregister when done.

A lot of efforts have been done to make the API more friendly, one of the great efforts is the Facebook’s Objective-c library KVOController. It’s a great library, but it is still written in Objective-c which means it’s lacking the power of swift.

With the inspiration of the Facebook’s KVOController, I’ve implemented a similar library that uses generics. Look at KVOController-Swift it uses the power of generics to give to the observer a strongly typed new/old values. Let’s see how to use it.

observe(retainedObservable: clock, keyPath: "date", options: [.New, .Initial])
    { [weak self] (observable: Clock, change: ChangeData) -> () in
        if let date = change.newValue {
            self?.timeLabel.text = formatter.stringFromDate(date)
        }
}

Yes, that’s it. All you need to do is import KVOController_Swift and then use the above method, it’s an NSObject extension. There is another version for nonretained observable of it, just the first parameter has a name nonretainedObservable. For this one, you will need to manually unobserve in your dealloc method or earlier as your need.

unobserve(clock, keyPath: "date")

Generics and Xcode 7 GM have also helped overcome a previous obstacle of not being able to create a generic class that inherits from NSObject. The previous idea is to use a proxy class that inherits from NSObject for use as an observer and then pass data to the generic class which will cast them to the appropriate type, all behind the scene (private classes and protocols). But now we don’t need to use this workaround anymore.

I might be talking in the next post about more complex generic data sources in more details. For now, if you have an idea or a library about practical examples for swift generics, please let us know.

Join the discussion on the forums here!

UNLEASH THE GIG ECONOMY. START A PROJECT OR TALK TO SALES
Close

Sign up for the Topcoder Monthly Customer Newsletter

Thank you

Your information has been successfully received

You will be redirected in 10 seconds