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:
- @objc cannot be applied to String as it's a struct, and Swift structs can't be exported to Objective-C
- TheĀ leftPad nameĀ should be prefixed, as per Objective-C recommendations
- String doesn't exist in Objective-C, but NSString does
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:
- The default character used in padding (String.padCharacter) isn't available
- The method name isn't the best for Objective-C
- This doesn't follow the recommended prefixed naming conventions
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.