Setting Objective-C names from Swift

When you write Swift class names, we follow the recommendation of not prefixing our class names or extensions with a two or three letter code. However, back in Objective-C, those conventions are quite important for a number of reasons, but principally to avoid naming collisions with other objects from different frameworks.

Let's consider the following code snippet that defines a movie:

class Movie {
let title: String
let director: String
let year: Int
/* Initializers */
}

As it is right now, it is not possible to use it in Objective-C as the Movie object doesn't inherit NSObject. Also, because you're exposing your class to Objective-C and not following the naming conventions of Objective-C with the prefix, we should probably rename the class with the @objc(...) declaration:

@objc(FVMovie)
class Movie: NSObject {
/* Original code */
}

The previous class will be exposed to Objective-C as FVMovie and not Movie and can be used properly and naturally in your Objective-C code.

Let's now imagine, you have written an extension for String in Swift called leftPad and you'll need it in Objective-C now. In Objective-C, we deal with NSString, not String, so the extensions are not exposed by default:

extension String {
static let padCharacter = " "
func leftPad(to length: Int, with character: String = .padCharacter) -> String {
/* Your left pad implementation */
}
}

There are multiple steps required to expose it to Objective-C:

Let's create an extension on NSString:

extension NSString {
func leftPad(to length: Int, with character: String = .padCharacter) -> String {
return (self as String).leftPad(to: length, with: character)
}
}

You'll realize that the method is still not exposed to Objective-C as the extension is not exposed by default. You can remedy that by marking the full extension @objc:

@objc
extension NSString { /* */ }

Now you can use the leftPad method in Objective-C:

NSString * padded = [@"Hello" leftPadTo:10 with:@" "];
// padded == @" Hello"

There are a few things we should improve before we can move on with that implementation:

In order to expose the default implementation that requires only the target length, we need to add an additional method:

extension NSString {
func leftPad(to length: Int) -> String {
return (self as String).leftPad(to: length)
}
}

This will leverage the original default implementation in Swift, so it's usable in Objective-C as well:

NSString * padded = [@"Hello" leftPadTo:10];
// padded == @" Hello"

Now let's put proper names on the methods:

extension NSString {
@objc(flv_leftPadToLength:)
func
leftPad(to length: Int) -> String

@objc(flv_leftPadToLength:withCharacter:)
func leftPad(to length: Int, with character: String) -> String
}

Now you'll be able to use those implementations in Objective-C with the following:

[@"Hello" flv_leftPadToLength:10]; // @"     Hello"
[@"Hello" flv_leftPadToLength:10 withCharacter:@"*"]; // @"*****Hello"

In this section, we've focused on adapting our Swift code to Objective-C so it's easier to use and more natural in the Objective-C style. In the next section, we'll focus on the opposite, making our Objective-C code more natural to Swift.