Migrating an Objective-C Class to Swift: a Piecemeal Approach
My usual strategy for this is to write a Swift extension for the Objective-C class. The new code goes into the extension. Where necessary,
@objc
annotations expose the extension’s code to Objective-C. This works great until the new code requires me to add a stored property to the class. It can’t go in the extension, I have to add it to the Objective-C class definition.This in turn means the property must have an Objective-C-compatible type, even if it’s only to be used internally by the Swift code. This is a fairly big limitation that I regularly run into: it means no structs, no enums with associated values, no generics, and more.
[…]
Here’s the workaround I use: in Swift, I define an Objective-C-compatible class that acts as a wrapper for all stored properties I want to use in my Swift extension. In Objective-C, I add a property for an instance of that class to the main class definition. Once that’s done, everything else happens in the Swift code: the properties can use Swift-only features (assuming you don’t need to access them from Objective-C) — only the class itself must be visible to Objective-C.
Associated objects may work too for simple cases. But another great solution I adopted successfully for a very large class was to rename ObjC
HHFooBar
into_HHFooBar
and redefineHHFooBar
as a Swift subclass of_HHFooBar
. If possible I take the opportunity to drop the prefix too.
Update (2018-08-10): Ole Begemann:
The subclassing approach is in many ways a cleaner design than the extension approach I discussed in the previous article. The ability to freely define stored properties is a clear win. And once you have finished the migration to Swift, simply delete the (now empty) superclass and you’re done.
The only significant downside seems to be the inability to call into the subclass code from the superclass (unless you work around it). The subclassing pattern may not be possible when the class you want to extend has Objective-C subclasses, however.