Skip to content

Porting to Swift? Do it Gradually Using Extensions: Part 2

As a refresher from Part 1, we’re trying to make the process of porting Objective-C to Swift code more gradual by porting classes method by method using Swift extensions. This is very helpful in keeping with the idea of “When it comes to porting, smaller steps are better.”

This only gets us so far, though.

Some of the classes you want to port are going to contain business logic for your apps. Model data, calculations on that model data, transformations, etc.

Unfortunately, you cannot move those properties to your extension; extensions don’t support adding stored properties to a class.

What does that mean? You can’t use Swift-only types for your properties, such as structs, enums, Swift arrays, optionals, etc., because they need to be accessible from both Objective-C and Swift.

Does that mean I’m stuck with ugly Objective-C types like NSArray, NSDictionary, etc. in our Swift code, until the entire class is ported?

Thankfully not. While you can’t add stored properties in an extension, you can add computed properties in an extension. By creating computed properties that bridge between the Objective-C properties and a Swift representation of that property, you can still port your class method by method, while still writing your new Swift methods using all of the power of the Swift type system.


This example is also a result of the porting work I am doing on 12 Strikes, my bowling score tracker app. This is continuing to port the BowlingGame class from Part 1.

One of the core structures of this class is an NSArray which stores a number of BowlingRoll objects, storing the result of each roll (number of pins knocked down, which ones, and other metadata).

I was doing a significant amount of calculation based on frames, which could have a number of specific states, and could be scored by expressing either which pins were knocked down, or just how many. But no advanced enums in Objective-C meant: NSArray of BowlingRolls. I wanted to do better in Swift, and represent the structures this way to greatly simplify the calculations:

enum RollResult {
    case score(_ score: Int)
    case pins(_ pins: Int)

    var score: Int {
        switch self {
        case .score(let score):
            return score
        case .pins(let pins):
            return numberBitsSet(pins)
        }
    }
}
	
enum FrameResult {
    case incomplete0
    case incomplete1(_ roll: RollResult)
	
    case open(_ roll1: RollResult, _ roll2: RollResult)
    case spare(_ roll: RollResult)
    case strike
}

There would be no way I could port this property on its own, because it would be impossible to use those advanced enums in Objective-C, and I wasn’t ready to port everything that depended on this important property into Swift immediately (which is almost the entire class).

Enter our solution: the computed property.

Once I defined these enums for the new data structure I wanted, I created a computed property in my BowlingGame extension called frameResults that translates the existing NSArray of BowlingRolls into a [FrameResult] usable by Swift:

var frameResults: [FrameResult] {
    var result: [FrameResult] = []
	
    for i in stride(from: 0, to: 24, by: 2) {
        // as! used here for code brevity, do something safer in real apps!
        BowlingRoll first = record[i] as! BowlingRoll
        BowlingRoll second = record[i] as! BowlingRoll
        // ...complicated translation code omitted, that
        // sets a var frameResult: FrameResult
        result.append(frameResult)
    }
    return result
}

From there, I can port any Objective-C methods into Swift one by one, using the new, easier to use Swift computed property in the code I ported, while not disturbing any of the existing Objective-C code.

Eventually, once all of the Objective-C code is ported, this computed property can finally become a regular stored property in Swift, without disturbing any of the Swift code already relying on it.

Side note: you may have noticed in this case, I have a read-only property. Because computed properties also supports setters, it could be written to support writing back to that property as well, although with NSArray and NSDictionary, this would be somewhat more complicated, and possibly not worth the added effort.


In the third and final part, we’ll cover issues when trying to port UIKit classes to Swift. Again, it requires some techniques that aren’t immediately obvious, but using them, you’ll be able to port those classes gradually as well. Sign up for the newsletter to be notified when those parts come out.

Comments are closed.