Is it possible to write custom conversion (casting) operator in swift
? Especially I\'m looking for enums conversion, ex:
enum MyEnum : Int {
Disclaimer/TL;DR! This answer pertains to the technical question as to whether we can possibly implement implicit bridging mechanisms between different Swift types ourself. The answer is: for some cases, yes, but only in a limited sense and by means of "hacks": do not use this is production code!
NSNumber
, NSString
...)As MartinR writes in his comment, custom conversion methods are not present for (native) Swift.
For the technical discussion we can, however, (ab)use the internal protocol _ObjectiveCBridgeable
to allow implicit bridging from your enum to Obj-C objects, in this case e.g. NSString
. For a more detailed Q&A of the subject of the internal protocol _ObjectiveCBridgeable
, see
Before proceeding, I'll quote a disclaimer from my answer in thread above:
... note that
_ObjectiveCBridgeable
is an internal/hidden protocol (_UnderScorePreFixedProtocol
), so solutions based on it might break without warning in upcoming Swift versions.
Example #1: implementing implicit bridging of your enum to NSString
First lets add a failable initializer to your enum, allowing (attempted) initialization by String
instances:
import Foundation
enum MyEnum: Int {
case Case1 = 0
case Case2
init?(string: String) {
switch string {
case "Case 1": self = .Case1
case "Case 2": self = .Case2
default: return nil
}
}
}
Next up, let MyEnum
conform to _ObjectiveCBridgeable
, as described in more detail in the thread linked to above
extension MyEnum: _ObjectiveCBridgeable {
typealias _ObjectiveCType = NSString
static func _isBridgedToObjectiveC() -> Bool {
return true
}
static func _getObjectiveCType() -> Any.Type {
return _ObjectiveCType.self
}
func _bridgeToObjectiveC() -> _ObjectiveCType {
return NSString(string: "Case \(self.rawValue+1)")
}
static func _forceBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) {
result = MyEnum(string: source as String)
}
static func _conditionallyBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) -> Bool {
self._forceBridgeFromObjectiveC(source, result: &result)
return true
}
}
With the conformance above, we can now make use of implicit bridging from MyEnum
instances to NSString
/* example usage */
var myCase: MyEnum = .Case1
var enumNSstr: NSString = myCase // MyEnum -> NSString, implicit
print(enumNSstr) // Case 1
enumNSstr = "Case 2"
// NSString -> MyEnum, by type conversion (castable)
myCase = (enumNSstr as MyEnum) ?? .Case1
print(myCase) // Case 2
Example #2: implementing implicit bridging of your enum to a custom Swift native type
We may even abuse the _ObjectiveCBridgeable
protocol further, using its (deep backend) mechanisms to implement implicit bridging between two native Swift types, with the limitation that the type bridged to must be a reference type (specifically: instances of the type must be representable by AnyObject
, hence the reference type limitation).
Let MyEnum
be as defined above, but additionally, define a reference (class) type Foo
, and conform MyEnum
to _ObjectiveCBridgeable
with the bridged to type, _ObjectiveCType
being set to Foo
.
class Foo {
var bar: String
init(bar: String) { self.bar = bar }
}
extension MyEnum: _ObjectiveCBridgeable {
typealias _ObjectiveCType = Foo
static func _isBridgedToObjectiveC() -> Bool {
return true
}
static func _getObjectiveCType() -> Any.Type {
return _ObjectiveCType.self
}
func _bridgeToObjectiveC() -> _ObjectiveCType {
return Foo(bar: "Case \(self.rawValue+1)")
}
static func _forceBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) {
result = MyEnum(string: source.bar)
}
static func _conditionallyBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) -> Bool {
self._forceBridgeFromObjectiveC(source, result: &result)
return true
}
}
We can now make use of implicit bridging from MyEnum
instances to Foo
/* example usage */
var myCase: MyEnum = .Case1
var myFoo: Foo = myCase // MyEnum -> Foo, implicit
print(myFoo.bar) // Case 1
myFoo.bar = "Case 2"
// Foo -> MyEnum, by type conversion (castable)
myCase = (myFoo as? MyEnum) ?? .Case1
print(myCase) // Case 2
Finally note that you may, for any given type (say, MyEnum
), naturally only implement implicit bridging to a single other (reference) type; since you can only conform to _ObjectiveCType
once (for a unique type for the typealias _ObjectiveCType
), otherwise yielding a compile time error for redundant protocol conformance.
The above is tested for Swift 2.2.