Using a Swift Integer enum in Objective C (the only Swift enum type that can be used in Objective C) is problematic. A public Objective C property for the enum can't be created directly. Consider:
// In a Swift file enum StringStyle: Int { case none case bold case italic } // In an Objective C header file // The Swift header cannot be imported in an Objective C header file... #import "Project-Swift.h" // Can't do this @interface MyClass: NSObject { // ...so a property of the enum type cannot be declared // because the enum type is not visible here without the Swift.h import @property (nonatomic, assign) StringStyle style; // Can't do this }
One work around is to declare an NSInteger in the Objective C header file as a stand in for the enum type instead:
// In an Objective C header file @property (nonatomic, assign) NSInteger style;
Unfortunately, any function that moves the NSInteger/enum between Swift and Objective C code will require conversion between the Objective C NSInteger and the Swift enum type.
This is a pain.
However, the effect of a public Swift enum property in Objective C code can still be achieved. The conversion between an NSInteger and the Swift enum can be centralized, thereby simplifying all bridging code between Objective C and Swift. This solution works through a Swift extension on the Objective C class that provides the enum property.
Ah, but a new property can't be declared through an extension! This is true, but easily worked around.
A computed property can be declared that creates an associated object as the backing store for the property. It will appear to be a public property of the Objective C class and of the enum type. Client code will not need to perform conversions between NSInteger and Swift enum representations since client code will only see a Swift enum type, as it should. The enum values can be used directly within and passed between both Objective C and Swift code. Here is an example of the computed property in an extension of the Objective C class:
extension MyClass { // Create a key to reference the associated object private struct Key { static var style = "StringStyle" } // This will appear as a public property on instances of the Objective C class @objc var style: StringStyle { get { guard let value = objc_getAssociatedObject(self, &Key.style) as? NSNumber, let style = StringStyle(rawValue: value.intValue) else { // Default value when no value has been set for the property before return .none } return style } set { objc_setAssociatedObject(self, &Key.style, NSNumber(value: newValue.rawValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } }
As shown below, the computed property can be used directly from both Objective C and Swift code.
// In an Objective C implementation file MyClass *myClass = [[MyClass alloc] init]; myClass.style = StringStyleBold; NSLog(@"Style: %ld", myClass.style); // In a Swift file let myClass = MyClass() myClass.style = .bold print("Style: \(myClass.style)")
I've found this technique especially helpful in a mixed Swift/Objective C code base where new functionality is being added in Swift, but Objective C code that can't be converted yet needs to take advantage of new functionality too. Eventually, when all Objective C code is converted, it's a simple task to replace the computed property with a true property since no code outside of the property needs to be changed.