Conversion Operator in Swift

Conversion operator in swift

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!

Swift internal protocol abuse: we may implement implicit mechanisms to Obj-C objects (e.g. 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

  • Is it possible to replicate Swifts automatic numeric value bridging to Foundation (NSNumber) for (U)Int8/16/32/64 types?

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.

Can you overload type-casting operators in Swift?

No, the language doesn't provide such functionality for custom types. There is bridging between Objective-C collections and Swift collections but that's baked in and not customizable.

// Swift array of `String` elements
let swiftArray: [String] = ["Bob", "John"]

// Obj-C array of `NSString` elements, but element type information
// is not known to the compiler, so it behaves like an opaque NSArray
let nsArray: NSArray = ["Kate", "Betty"]

// Obj-C array of an `NSString` and an `NSNumber`, element type
// information is not known to the compiler
let heterogeneousNSArray: NSArray = ["World", 3]

// Casting with `as` is enough since we're going from Swift array to NSArray
let castedNSArray: NSArray = swiftArray as NSArray

// Force casting with `as!` is required as element type information
// of Obj-C array can not be known at compile time
let castedSwiftArray: [String] = nsArray as! [String]

// Obj-C arrays can not contain primitive data types and can only
// contain objects, so we can cast with `as` without requiring a
// force-cast with `!` if we want to cast to [AnyObject]
let heterogeneousCastedNSArray: [AnyObject] = heterogeneousNSArray as [AnyObject]

Documentation for type casting is available here.

I think you can achieve what you want to do with initializers.

extension X {
init(string: String) {
self = X(string)
}
}

extension String {
init(x: X) {
// toString is implemented elsewhere
self = x.toString
}
}

let x = X()
let string = "Bobby"

let xFromString: X = X(string: string)
let stringFromX: String = String(x: x)

Not directly related to your question but there is also a family of protocols that start with ExpressibleBy..., enabling you to do things like the following:
Let's say we want to initialize strings from integer literals. We can do that by conforming to and implementing ExpressibleByIntegerLiteral

// Strings can not be initialized directly from integer literals
let s1: String = 3 // Error: Can not convert value of type 'Int' to specified type 'String'

// Conform to `ExpressibleByIntegerLiteral` and implement it
extension String: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
// String has an initializer that takes an Int, we can use that to
// create a string
self = String(value)
}
}

// No error, s2 is the string "4"
let s2: String = 4

A nice use case for ExpressibleByStringLiteral can be found here.

Convert String to operator - swift

try something like this

var s = " 1 + 9 X 8"
s = s.replacingOccurrences(of: "X", with: "*")
let expn = NSExpression(format:s)
print("\(expn.expressionValue(with: nil, context: nil) ?? 0)")

How to define an explicit casting operator in swift?

I don't know this is what you want but, you can implement cast like Int initializer:

extension Int {
init(_ foo:Foo) {
self = foo.i
}
}

let f = Foo()
f.i = 12
let intVal = Int(f) // -> 12

Does Swift support implicit conversion?

There is no implicitly cast in Swift.

Easy way of conversion in swift is using constructor of particular type.

Like if you want to get Float from double then you can use Float(doubleValue) and Same way if you want to convert float to integer then you can use Int(floatValue).

In your case:

let intValue = UInt8(doubleValue)

Beware that you will lose any value after the decimal point. So, choose a better way. Above conversion is just to help you in understanding.

Note that Swift always chooses Double (rather than Float) when inferring the type of floating-point numbers.

Swift simplify optional Int to String conversion with nil coalescing operator

Here's one solution:

let str = "\(num.map { String($0) } ?? "?") foo"

This returns "? foo" if num is nil or it returns "42 foo" if num is set to 42.

Swift-Binary operator cannot be applied to operands, when converting degrees to radians

let degree = arc4random_uniform(360)

let radian = Double(degree) * .pi/180

you need to convert the degree to double before the multiplication .

from apple swift book:

Integer and Floating-Point Conversion

Conversions between integer and floating-point numeric types must be made explicit:

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine

// pi equals 3.14159, and is inferred to be of type Double

Here, the value of the constant three is used to create a new value of type Double, so that both sides of
the addition are of the same type. Without this conversion in place, the addition would not be allowed.
Floating-point to integer conversion must also be made explicit. An integer type can be initialized
with a Double or Float value:

1 let integerPi = Int(pi)
2 // integerPi equals 3, and is inferred to be of type Int

Floating-point values are always truncated when used to initialize a new integer value in this way.
This means that 4.75 becomes 4, and -3.9 becomes -3.



Related Topics



Leave a reply



Submit