is there exist some alternative of Enum.GetName(Type, Object) method in Swift? - 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"

Related

How to use generic default parameters

This is my code:
class Person {
init<T: RawRepresentable>(raw: T = Child.johnDoe) {}
}
enum Child: String {
case johnDoe
}
It doesn't compile. The error is:
Default argument value of type 'Child' cannot be converted to type 'T'
Why can't it be converted? According to the docs, Child.someEnum is RawRepresentable:
Enumerations with Raw Values For any enumeration with a string,
integer, or floating-point raw type, the Swift compiler automatically
adds RawRepresentable conformance. When defining your own custom
enumeration, you give it a raw type by specifying the raw type as the
first item in the enumeration’s type inheritance list.
This also compiles:
class Person {
static func accept<T: RawRepresentable>(raw: T) where T.RawValue == String {}
}
enum Child: String {
case johnDoe
}
Person.accept(raw: Child.johnDoe)
Why doesn't it work as a default parameter?
Use case: I want to accept any RawPresentable value, so I can extract the rawValue from it. I want to provide a default value (always "") (I just create a struct with rawValue = ""). I do not want to create multiple initializers, since I got some subclasses and that would get a mess. The best for me is just to provide a default RawRepresentable object.
When I add a cast:
init(ty: T = (Child.johnDoe as! T)) where T.RawValue == String {
}
Or make it nillable:
(ty: T? = nil)
It compiles. But now I can not call:
let x = Person()
It gives the error:
Generic parameter 'T' could not be inferred
This is certainly possible. However, you have to use your own protocol and add the default value to that protocol:
protocol MyRawRepresentable: RawRepresentable {
static var defaultValue: Self { get }
}
class Person {
init<T: MyRawRepresentable>(raw: T = T.defaultValue) {}
}
enum Child: String, MyRawRepresentable {
case johnDoe
static let defaultValue: Child = .johnDoe
}
There is another issue though. How will you specify the generic type if you use the default parameter value and all you will have will be just Person.init()?
The only solution I see is to also specify a default generic type which means you actually want:
class Person {
init<T: RawRepresentable>(raw: T) {
}
convenience init() {
self.init(raw: Child.johnDoe)
}
}
Unless you actually want to make Person itself a generic class because then you could just use
Person<Child>.init()

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

Swift "Enum" base object

If I want to add an extension to every object I can do the following:
extension AnyObject
{
func myFunc() { ... }
}
Is there something similar where I can add a function to every Enum? In other words, what is the base "class" for every enum?
First of all, note that you cannot do an extension to AnyObject as above, as AnyObject is a protected protocol (to which all classes implicitly conform) rather than a type. See e.g.
Is there a way to add an extension to AnyObject?
Now, you could, however, extend any specific type as you show above, e.g., extension Int { ... }. However enum is not a type; rather a "container" of ordered raw representable values. So a more valid comparison could be "If I want to add an extension to every class, by extension class ...", which is, naturally, trickier.
Now, all enumerations with a rawValue and an initializer by rawValue conforms to protocol RawRepresentable, so you could extend this protocol for specific types of raw values:
extension RawRepresentable where RawValue == Int {
var sqrt: Double {
return pow(Double(rawValue),(1/2))
}
}
And this extension property would be available to all enumerations that explicitly use the same raw value type, in this case, Int:
enum MyIntegerLiteralEnum : Int {
case One = 1
case Two
case Three
case Four
}
print(MyIntegerLiteralEnum.One.sqrt)
print(MyIntegerLiteralEnum.Two.sqrt)
print(MyIntegerLiteralEnum.Four.sqrt)
/* 1.0
1.4142135623731
2.0 */
As a disclaimer, note that this extension will be made available to all types that conforms to RawRepresentable with a rawValue of type Int, not only enum types. E.g.:
struct Foo : RawRepresentable {
typealias RawValue = Int
var bar : Int
var rawValue: RawValue {
get {
return bar
}
}
init(rawValue bar: Int) {
self.bar = bar
}
}
var a = Foo(rawValue: 16)
print(a.sqrt) // 4.0

Swift 2: How to refer to a type of an element in default implementation of a function in a protocol

I need to get some objects from REST API and map them to local objects using ObjectMapper.
These objects contain a number of enumerations. All of them are coming as integers and I want to map them to locally described enums.
To do that I have to describe standard transform function that is used by ObjectMapper.
enum Types: Int {
case Uno = 1
case Dos = 2
case Tres = 3
static var transform = TransformOf<Types,Int>(
fromJSON: {
$0 != nil
? Types(rawValue:$0!)
: nil
},
toJSON: { $0?.rawValue})
}
The problem is that I have a number of these enumerations and the function is totally same in all of them except that first argument in TransformOf<..> list is specific for each enum.
What I want to do is to create a common protocol with default implementation of that function, something like
protocol Transformable {
var transform: TransformOf<self.Type,Int> {
get {
return TransformOf<self.Type,Int>(
fromJSON: {
$0 != nil
? Types(rawValue:$0!)
: nil
},
toJSON: { $0?.rawValue})
}
}
}
...and then to apply the protocol with the implementation to all of the enumerations I have.
Obviously reference of self.Type is not working there and I just can't get how to generally refer to type of specific instance that will finally use the function? Probably I'm thinking wrong way of solving that problem.
I think what you're missing is the Self identifier. When implementing Generics, the Self keyword acts as a placeholder for the type that implements your protocol. (For more information)
In other words:
protocol Transformable {
var rawValue: Int { get }
init?(rawValue: Int)
func toJSON() -> Int
static func fromJSON(rawValue: Int) -> Self?
}
Each enum that conforms to the protocol Transformable will then have a static method that returns its own type.
Secondly, since this is Swift 2, you can implement a protocol extension:
extension Transformable {
func toJSON() -> Int {
return self.rawValue
}
static func fromJSON(rawValue: Int) -> Self? {
return Self(rawValue: rawValue)
}
}
Now all enums that conform to the protocol will convert themselves to and from Int:
enum Types: Int, Transformable {
case Uno = 1
case Dos = 2
case Tres = 3
//No extra implementation
}
enum OtherTypes: Int, Transformable {
case Cuatro = 4
case Cinco = 5
case Seis = 6
//No extra implementation
}
print(Types.fromJSON(1)!) //prints Uno
print(OtherTypes.fromJSON(4)!) //prints Cuatro
print(Types.fromJSON(4)!) /* throws an error, because the Types enum
does not recognise 4 as a raw value */

enum of non-literal values in Swift

Is there any way to map a non-literal value like tuple of dictionary to enums? Following code will throw Raw value for enum must be literal.
enum FileType {
case VIDEO = ["name": "Video", "contentTypeMatcher": "video/"]
case IMAGE = ["name": "Image", "contentTypeMatcher": "image/"]
case AUDIO = ["name": "Audio", "contentTypeMatcher": "aduio/"]
case PDF = ["name": "PDF", "contentTypeMatcher":"application/pdf"]
case TEXT = ["name": "Text", "contentTypeMatcher": "text/"]
case FOLDER= ["name": "Folder", "contentTypeMatcher" :"application/x-directory"]
case PLAIN = ["name": "Plain", "contentTypeMatcher": ""]
}
It's the same when I use tuples:
enum FileType {
case VIDEO = (name: "Video", contentTypeMatcher: "video/")
case IMAGE = (name: "Image", contentTypeMatcher: "image/")
case AUDIO = (name: "Audio", contentTypeMatcher: "aduio/")
case PDF = (name: "PDF", contentTypeMatcher:"application/pdf")
case TEXT = (name: "Text", contentTypeMatcher: "text/")
case FOLDER = (name: "Folder", contentTypeMatcher :"application/x-directory")
case PLAIN = (name: "Plain", contentTypeMatcher: "")
}
#Antonio gives workaround but does not answer the actual question.
Define your enum.
enum FileType {
case Image, Video
}
Give cases non-literal values, whatever type you want with conforming to RawRepresentable protocol. Do it by enum extension to have cleaner code.
extension FileType: RawRepresentable {
typealias Tuple = (name: String, contentTypeMatcher: String)
private static let allCases = [FileType.Image, .Video]
// MARK: RawRepresentable
typealias RawValue = Tuple
init?(rawValue: Tuple) {
guard let c = { () -> FileType? in
for iCase in FileType.allCases {
if rawValue == iCase.rawValue {
return iCase
}
}
return nil
}() else { return nil }
self = c
}
var rawValue: Tuple {
switch self {
case .Image: return Tuple("Image", "image/")
case .Video: return Tuple("Video", "video/")
}
}
}
To be able to match Tuple in switch, implement pattern matching operator.
private func ~= (lhs: FileType.Tuple, rhs: FileType.Tuple) -> Bool {
return lhs.contentTypeMatcher == rhs.contentTypeMatcher && lhs.name == rhs.name
}
And thats it...
let a = FileType.Image
print(a.rawValue.name) // "Image"
let b = FileType(rawValue: a.rawValue)!
print(a == b) // "true"
print(b.rawValue.contentTypeMatcher) // "image/"
Let's say I answered the question without questioning. Now... Enums (in Swift at least) are designed to have unique cases. Caveat to this workaround is that you can (I hope by accident) hold same rawValue for more cases. Generally your example code smells to me. Unless you (for very reasonable reason) need to create new enum value from tuple, consider redesign. If you want go with this workaround, I suggest (depends on project) to implement some check if all case raw values are unique. If not, consider this:
enum FileType {
case Video, Image
var name: String {
switch self {
case .Image: return "Image"
case .Video: return "Video"
}
var contentTypeMatcher: String {
switch self {
case .Image: return "image/"
case .Video: return "video/"
}
}
The language reference, when talking about Enumeration Declaration, clearly states that:
the raw-value type must conform to the Equatable protocol and one of the following literal-convertible protocols: IntegerLiteralConvertible for integer literals, FloatingPointLiteralConvertible for floating-point literals, StringLiteralConvertible for string literals that contain any number of characters, and ExtendedGraphemeClusterLiteralConvertible for string literals that contain only a single character.
So nothing else but literals can be used as raw values.
A possible workaround is to represent the dictionary as a string - for example, you can separate elements with commas, and key from value with colon:
enum FileType : String {
case VIDEO = "name:Video,contentTypeMatcher:video/"
case IMAGE = "name:Image,contentTypeMatcher:image/"
...
}
Then, using a computed property (or a method if you prefer), reconstruct the dictionary:
var dictValue: [String : String] {
var dict = [String : String]()
var elements = self.rawValue.componentsSeparatedByString(",")
for element in elements {
var parts = element.componentsSeparatedByString(":")
if parts.count == 2 {
dict[parts[0]] = parts[1]
}
}
return dict
}
My coworkers and I have been debating this topic recently as Swifts enum type is unique from other languages. In a language like Java where an enum is just a class that inherits from Enumeration, you can have static non-literal values assigned to each case.
In swift, we can not find a supported way to do this. From Swift documentation:
If a value (known as a “raw” value) is provided for each enumeration case, the value can be a string, a character, or a value of any integer or floating-point type.
Alternatively, enumeration cases can specify associated values of any type to be stored along with each different case value, much as unions or variants do in other languages. You can define a common set of related cases as part of one enumeration, each of which has a different set of values of appropriate types associated with it.
The second paragraph may seem like it can do what #Antonio asked but it is not. In swift's example:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
But each enum is an instance with different value types (tuple vs string) and the values within them are different based on each instance of the enum created.
I wanted something that allowed more than the limited raw values but each enum contained the same value type (ie tuple, object, etc...) and is static.
With my coworkers input we came up with two options that have different tradeoffs.
The first is a private static dictionary by the enum that holds the value type you desire:
enum FooBarDict {
case foo
case bar
private static let dict = [foo: (x: 42, y: "The answer to life, the universe, and everything"),
bar: (x: 420, y: "Party time")]
var x: Int? { return FooBarDict.dict[self]?.x }
var y: String? { return FooBarDict.dict[self]?.y }
}
Our issue with this implementation is that there's no way at compile time that you can ensure that the developer has exhaustively included all of the enum cases. This means that any properties you right must be optional or return a default.
To resolve that issue we came up with the following:
enum FooBarFunc {
case foo
case bar
typealias Values = (x: Int, y: String)
private func getValues() -> Values {
switch self {
case .foo: return (x: 42, y: "The answer to life, the universe, and everything")
case .bar: return (x: 420, y: "Party time")
}
}
var x: Int { return getValues().x }
var y: String { return getValues().y }
}
Now it is exhaustive due to the switch statement in the getValues! A developer can not add a new case and compile without explicitly adding the value type.
My (perhaps unfounded) fear with this approach is that it may be both slower due to the switch statement lookup - although this may be optimized to be as fast as the dictionary lookup. And I am unsure if it will create a new value each time a enum property is requested. I'm sure I could find answers to both of these concerns but I've already wasted too much time on it.
To be honest, I hope I'm just missing something about the language and this is easily done in another way.