Can you overload type-casting operators in Swift? - swift

I'd like to implement automatic type conversions between known types in Swift. The C# way of doing it has been overloading type-casting operators. If I want my X type to be cross-assignable with, say, string, I would write:
public class X
{
public static implicit operator string(X value)
{
return value.ToString();
}
public static implicit operator X(string value)
{
return new X(value);
}
}
After that I could write stuff like:
string s = new X();
X myObj = s;
and they would be automatically converted. Is that possible in any way 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.

Related

Declare an array of a generic struct specialized with different types

I know that Swift does not have wildcard types to specialize generic types. I have a problem I would know how to solve using them in a language like Java.
In the following example I'm trying to define a struct Property which encapsulates access to a property of a type T to perform two operations, getting the hash value of the value of that property on an instance of T and comparing the values of the property on two instances of T. To allow this, the struct contains a property getter holding a function which returns the value of the specific property on an instance. Because of this, the struct needs to be generic not only in T but also the type E of the property.
In the struct Test, I would like to define a static property properties holding an array of Property instances, one for each property of Test. I cannot see a way to do that because I don't know what type to use for properties or how to "hide" the type parameter E so that it does not need to be specified in the declaration of properties.
// Struct which represents a property of type T with a value of type E.
// Aside from initialization only T is visible from outside.
public struct Property<T, E: Hashable> {
let getter: (T) -> E
func getMemberHashValue(instance: T) -> Int {
return getter(instance).hashValue
}
func equalsMembers(instance1: T, instance2: T) -> Bool {
return getter(instance1) == getter(instance2)
}
}
struct Test {
// Some properties with different types
let a: Int
let b: Double
// Array of Property instances for all properties of Test.
// Not valid syntax.
let properties: [Property<Test, ?>] = [
Property(getter: { (instance: Test) in instance.a }),
Property(getter: { (instance: Test) in instance.b })]
}
I know that I could in this case substitute AnyHashable for the second type parameter in the declaration of properties as it has no associated types, but I would like to find a general solution I could apply in cases not involving Hashable.
Is there a way to change this example to allow such a property properties to be defined holding multiple Property instances for properties of different types?
Overview:
I am not sure I understood your question correctly.
Might be worth stating why you would like a heterogeneous array, would lose all type safety in the process.
Might be revisiting the design
Option 1:
If it is absolutely required, if so just use an NSArray / NSMutableArray
Option 2:
If you want to access properties of a class / struct / enum dynamically, you could do so with KeyPath.
Code:
struct Test : Hashable {
// Some properties with different types
let a: Int
let b: Double
let c: String
//This function will return any property of Test instance
func property<T>(forKeyPath keyPath: KeyPath<Test, T>) -> T {
return self[keyPath: keyPath]
}
//MARK: Hashable
var hashValue: Int {
return a.hashValue ^ b.hashValue ^ c.hashValue
}
//MARK: Equatable
static func ==(lhs: Test, rhs: Test) -> Bool {
return lhs.a == rhs.a &&
lhs.b == rhs.b &&
lhs.c == rhs.c
}
}
let test1 = Test(a: 10, b: 22.22, c: "aaaa")
let cValue = test1.property(forKeyPath: \.c) //aaaa
let cHashValue = test1.property(forKeyPath: \.c.hashValue) //Hash value of aaaa
let someInt = 10 //This is also Hashable
var mixedArray = [AnyHashable]()
mixedArray.append(test1)
mixedArray.append(someInt)
Refer:
If this matches your requirement, then please read about KeyPath, PartialKeyPath.
Version:
Swift 4

is there exist some alternative of Enum.GetName(Type, Object) method in Swift?

in c# is possible to get enum name of any type with method
Enum.GetName (Type, Object)
is there possible to do the same in Swift?
As example I have some enum that must have Int raw value, but also I need it's own name.
how to do this in Swift 3?
Pre-requisite: you have an enumeration type with an underlying RawValue; i.e., an enumeration conforming to RawRepresentable.
The C# method Enum.GetName Method (Type, Object) is described as follows:
Enum.GetName Method (Type, Object)
public static string GetName(
Type enumType,
object value
)
Parameters
enumType
Type: System.Type
An enumeration type.
value
Type: System.Object
The value of a particular enumerated constant in terms of its underlying type.
Return Value
- Type: System.String
- A string containing the name of the enumerated constant in enumType whose value is value; or null if no such constant is
found.
"Translated" into Swift, this method takes an enum type as first argument, and a value of the underlying type of the enum as its second one; where the latter would (for the pre-requisites of RawRepresentable conformance) translate into a value of the RawValue type of the enum in Swift.
Finally, the return type System.String (with possible null return) would translate to an optional String return type in Swift (String?).
Knitting these observations together, you could implement your similar getName method (for finding String representation of your cases represented as YourEnum.RawValue instances at runtime) by combining the init?(rawValue:) blueprinted in RawRepresentable with the init(describing:) initializer of String:
func getName<T: RawRepresentable>(_ _: T.Type, rawValue: T.RawValue) -> String? {
if let enumCase = T(rawValue: rawValue) {
return String(describing: enumCase)
}
return nil
}
/* example setup */
enum Rank: Int {
case n2 = 2,n3,n4,n5,n6,n7,n8,n9,n10,J,Q,K,A
}
/* example usage */
let someRawValue = 7
if let caseName = getName(Rank.self, rawValue: someRawValue) {
print(caseName) // n7
}
This is contrived and coerced "translation" however, as Swift enum:s are quite different (and more powerful) than those of C#. You would generally have an actual instance instance of the enum (e.g. Rank.n7) rather than one of its RawValue (e.g. 7), in which case you could simply use the init(describing:) initializer of String directly as described in the answer of #AlexanderMomchliov.
To get an enumeration case's name as a String, you can use init(describing:) on String:
enum Foo: Int {
case A
case B
case C
}
let s = String(describing: Foo.A)
print(s) // "A"
You can bake this into the enum:
enum Foo: Int {
case A
case B
case C
var asString: String { return String(describing: self) }
}
let s = Foo.A.asString
print(s) // "A"

Error: Argument type double/string etc. does not conform to expected type "AnyObject"

I watched some iOS programming tutorial and have a question on "AnyObject".
The bridging does not work.
I have the following code:
import Foundation
class CalculatorBrain
{
private var accumulator = 0.0
var internalProgram = [AnyObject]()
func setOperand (operand: Double) {
accumulator = operand
internalProgram.append(operand)
}
// ...
}
Same for string...
func performOperation (symbol: String) {
internalProgram.append(symbol)
}
I know about NSString and the reference type vs. struct thing, and that double and string are no reference types.
But anyway in the tutorial the bridging worked fine...
What could be the reason for my failure?
As you correctly say, Double and String are not reference types in Swift – they are structs. Therefore you cannot directly store them in an [AnyObject], you would first need to bridge them to Objective-C in order to do so.
Although bridging to Objective-C in this case is unnecessary – if you want an array of heterogenous types, including value types, then you can use an [Any]:
var internalProgram = [Any]()
However, from what I can tell, you don't want a array of anything (it's very rarely a good idea) – you just want an array that can contain a Double or a String.
You can describe this with an enum:
enum OperationArgument { // feel free to give me a better name
case operand(Double)
case symbol(String)
}
And now you can define an array of it:
var internalProgram = [OperationArgument]()
func setOperand (operand: Double) {
internalProgram.append(.operand(operand))
}
func performOperation (symbol: String) {
internalProgram.append(.symbol(symbol))
}
Now instead of conditional type-casting to get back the type of each element, you can just use a switch or an if case:
for element in internalProgram {
switch element {
case let .operand(operand):
print(operand)
case let .symbol(symbol):
print(symbol)
}
if case let .operand(operand) = element {
print(operand)
}
}
To use a Swift value type as an AnyObject, in Swift 3 you need to explicitly cast it to the old Objective-C object type.
So this
internalProgram.append(operand)
becomes this
internalProgram.append(operand as NSNumber)
And this
internalProgram.append(symbol)
becomes this
internalProgram.append(symbol as NSString)
The tutorial you are talking about has probably been written with Swift 2 where you just needed to import Foundation.
var internalProgram = [AnyObject]()
func setOperand (operand: Double) {
// you have cast operand as AnyObject !!
internalProgram.append(operand as AnyObject)
}
setOperand(operand: 10)
print(internalProgram, type(of: internalProgram)) // [10] Array<AnyObject>

How to get the unwrapped type from an optional type in Swift?

I'm trying to get an unwrapped type from an optional type in runtime.
The following code would print the type of a as Optional<String>.
class MySubClass: MyClass {
var a: String? = nil
}
var a = MySubClass()
let mirror = Mirror(reflecting: a)
for child in mirror.children {
print(child.value.dynamicType)
}
Now I want to unwrap the type and get String, what should I do to make this happen in runtime?
Assuming you have an optional
let someVar: String?
then print(type(of: someVar)) will print
Optional<String>
but if you add the following extension to Optional
protocol OptionalProtocol {
func wrappedType() -> Any.Type
}
extension Optional: OptionalProtocol {
func wrappedType() -> Any.Type {
return Wrapped.self
}
}
then print(someVar.wrappedType()) will print
String
No reflection whatsoever
Summary
As long as the optional is not referenced by Any or AnyObject the code will work fine.
For Any you will have to cast it to OptionalProtocol first. Running
let someVar: String?
let anyVar = someVar as Any
if let op = anyVar as? OptionalProtocol {
print(op.wrappedType())
}
will print
String
As for AnyObject, strangely enough (at least for me), it doesn't cast to OptionalProtocol.
The original StackOverflow answer can be found here
I played with your idea a little bit, but I think there isn't a real way to do that, since you can't get the type of the associated value of an enumeration, yet. Hence Optionals are basically Enumerations, we have a problem here.
My idea would be to test for all possible value types in your model objects could be or hold. Like:
let myModelObject:Any? = someWayToGetTheData()
if let aString = myModelObject as? String {
// do everything you need to store a string
}
else if let anInteger = myModelObject as? Int {
// do everything you need to store an integer
}
// and so on ...
Since your json and your model must have a predefined number of supported conversions that is a possible way, and as far as I understand your original problem, it's basically as useful as testing for the dynamic associated value type of an Optional Enumeration, which will lead into a chain of if-else statements as well.
You can either unwrap the optional explicitly with a bang (!) or with an if let.
For example:
var foo: String? = nil
if foo == nil {
print("foo is nil")
foo = "bar"
}
let fooBang = foo!
print("fooBang: \(fooBang)")
if let ifLetFoo = foo {
print("ifLetFoo: \(ifLetFoo)")
}
This will print:
foo is nil
fooBang: bar
ifLetFoo: bar
In your context, I think print(child.value.dynamicType!) might be what you're looking for.
If you cast the value to the non-optional String, it will print you the unwrapped type.
let mirror = Mirror(reflecting: a)
for child in mirror.children {
print(String(child.value).dynamicType) //String
}
Or you can play with the String and get the type from the Optional type.
class MySubClass: MyClass {
var a: Int? = nil
}
var a = MySubClass()
let mirror = Mirror(reflecting: a)
for child in mirror.children {
let typeArr = String(child.value.dynamicType).characters.split{$0 == "<"}.map(String.init)
let typeArr2 = typeArr[1].characters.split{$0 == ">"}.map(String.init)
print(typeArr2[0]) // print: Int
}
Sorry this is lame but you can do something like this.

Swift Cast Generics Type

I'm trying to cast a generic type to its super class.
class Foo : NSObject {
}
class Test<T> {
let value: T
init(_ value: T) {
self.value = value
}
}
let c = Test(Foo())
let v = c as Test<AnyObject>
But on the line
let v = c as Test<AnyObject>
I get 'Foo' is not identical to 'AnyObject'
While I can do that with built-in Arrays
let array1 = [Foo(), Foo()]
let array2 = array1 as [AnyObject]
Arrays are special-cased in Swift to be able to have this behaviour. You won't be able to replicate it with your own classes, unfortunately.
To get similar behaviour, you could give your type another init method:
class Test<T> {
let value: T
init(_ value: T) {
self.value = value
}
init<U>(other: Test<U>) {
// in Swift 1.2 you will need as! here
self.value = other.value as T
}
}
let c = Test(Foo())
let v = Test<AnyObject>(other: c) // this will work ok
Note though that this is unsafe (i.e. will assert at runtime) in cases where you want to convert to an incompatible type (and the alternative, a failable initializer, will also have complications)