Get rawValue from enum in a generic function
Unfortunately, this doesn't look like it's possible in Swift at this point, but I've thought about your problem for a while, and I'll propose 3 ways that the Swift team could enable you to solve this problem.
Fix the mirror for enums. The most straightforward solution is one that I'm sure you already tried. You're trying to build a reflection library, and you'd like to reflect an
Any
value to see if it's an enum, and if it is, you'd like to see if it has a raw value. TherawValue
property should be accessible via this code:let mirror = reflect(theEnum) // theEnum is of Any type
for i in 0..<mirror.count {
if mirror[i].0 == "rawValue" {
switch mirror[i].1.value {
case let s as String:
return s
case let csc as CustomStringConvertible:
return csc.description
default:
return nil
}
}
}
However, this doesn't work. You'll find that the mirror has a count
of 0
. I really think that this is an oversight on the part of the Swift team in their implementation of Swift._EnumMirror
, and I'll be filing a radar about this. rawValue
is definitely a legitimate property. It is a weird scenario because enums aren't allowed to have other stored properties. Also, your enum's declaration never explicitly conforms to RawRepresentable
, nor does it declare the rawValue
property. The compiler just infers that when you type enum MyEnum: String
or : Int
or whatever. In my tests, it appears that it shouldn't matter whether the property is defined in a protocol or is an instance of an associated type, it should still be a property represented in the mirror.
Allow for protocol types with defined associated types. As I mentioned in my comment above, it's a limitation in Swift that you cannot cast to a protocol type that has associated type requirements. You can't simply cast to
RawRepresentable
because Swift doesn't know what type therawValue
property will return. Syntax such asRawRepresentable<where RawValue == String>
is conceivable, or perhapsprotocol<RawRepresentable where RawValue == String>
. If this were possible, you could try to cast to the type through a switch statement as in:switch theEnum {
case let rawEnum as protocol<RawRepresentable where RawValue == String>:
return rawEnum.rawValue
// And so on
}
But that's not defined in Swift. And if you just try to cast to RawRepresentable
, the Swift compiler tells you that you can only use this in a generic function, but as I look at your code, that's only led you down a rabbit-hole. Generic functions need type information at compile-time in order to work, and that's exactly what you don't have working with Any
instances.
The Swift team could change protocols to be more like generic classes and structs. For example MyGenericStruct<MyType>
and MyGenericClass<MyType>
are legitimately specialized concrete types that you can make a runtime check on and cast to. However, the Swift team may have good reasons for not wanting to do this with protocols. Specialized versions of protocols (i.e. protocol references with known associated types) still wouldn't be concrete types. I wouldn't hold my breath for this ability. I consider this the weakest of my proposed solutions.
Extend protocols to conform to protocols. I really thought I could make this solution work for you, but alas no. Since Swift's built-in mirror for enums doesn't deliver on the
rawValue
, I thought why not implement my own generic mirror:struct RawRepresentableMirror<T: RawRepresentable>: MirrorType {
private let realValue: T
init(_ value: T) {
realValue = value
}
var value: Any { return realValue }
var valueType: Any.Type { return T.self }
var objectIdentifier: ObjectIdentifier? { return nil }
var disposition: MirrorDisposition { return .Enum }
var count: Int { return 1 }
subscript(index: Int) -> (String, MirrorType) {
switch index {
case 0:
return ("rawValue", reflect(realValue.rawValue))
default:
fatalError("Index out of range")
}
}
var summary: String {
return "Raw Representable Enum: \(realValue)"
}
var quickLookObject: QuickLookObject? {
return QuickLookObject.Text(summary)
}
}
Great! Now all we have to do is extend RawRepresentable
to be Reflectable
so that we can first cast theEnum as Reflectable
(no associated type required) and then call reflect(theEnum)
to give us our awesome custom mirror:
extension RawRepresentable: Reflectable {
func getMirror() -> MirrorType {
return RawRepresentableMirror(self)
}
}
Compiler error: Extension of protocol 'RawRepresentable' cannot have
an inheritance clause
Whaaaat?! We can extend concrete types to implement new protocols, such as:
extension MyClass: MyProtocol {
// Conform to new protocol
}
As of Swift 2, we can extend protocols to give concrete implementations of functions, such as:
extension MyProtocol {
// Default implementations for MyProtocol
}
I thought for sure we could extend protocols to implement other protocols, but apparently not! I see no reason why we couldn't have Swift do this. I think this would very much fit in with the protocol-oriented programming paradigm that was the talk of WWDC 2015. Concrete types that implement the original protocol would get a new protocol conformance for free, but the concrete type is also free to define its own versions of the new protocol's methods. I'll definitely be filing an enhancement request for this because I think it could be a powerful feature. In fact, I think it could be very useful in your reflection library.
Edit: Thinking back on my dissatisfaction with answer 2, I think there's a more elegant and realistic possibility for working with these protocols broadly, and that actually combines my idea from answer 3 about extending protocols to conform to new protocols. The idea is to have protocols with associated types conforming to new protocols that retrieve type-erased properties of the original. Here is an example:
protocol AnyRawRepresentable {
var anyRawValue: Any { get }
}
extension RawRepresentable: AnyRawRepresentable {
var anyRawValue: Any {
return rawValue
}
}
Extending the protocol in this way wouldn't be extending inheritance per se. Rather it would be just saying to the compiler "Wherever there is a type that conforms to RawRepresentable
, make that type also conform to AnyRawRepresentable
with this default implementation." AnyRawRepresentable
wouldn't have associated type requirements, but can still retrieve rawValue
as an Any
. In our code, then:
if let anyRawEnum = theEnum as? AnyRawRepresentable { // Able to cast to
let anyRawValue = anyRawEnum.anyRawValue // anyRawValue is of type Any
switch anyRawValue {
case let s as String:
return s
case let csc as CustomStringConvertible:
return csc.description
default:
return nil
}
}
This kind of solution could be used broadly with any kind of protocol with associated types. I will likewise be including this idea in my proposal to the Swift team for extending protocols with protocol conformance.
Update: None of the above options are available as of Swift 4. I did not receive a response as to why the Mirror
on a RawRepresentable
enum does not contain its rawValue
. As for options #2 and #3, they are still within the realm of possibility for future releases of Swift. They have been mentioned on the Swift mailing list and in the Generics Manifesto document authored by Doug Gregor of the Swift team.
The proper term for option #2 ("Allow for protocol types with defined associated types") is generalized existentials. This would allow protocols with associated types to possibly automatically return Any
where there is an associated type or allow for syntax like the following:
anyEnum as? Any<RawRepresentable where .RawValue == String>
Option #3 has been mentioned occasionally on the mailing list, and it is a commonly rejected requested feature, but that is not to say it couldn't be included in future versions of Swift. In the Generics Manifesto, it is termed "Conditional conformances via protocol extensions". While recognizing its power as a feature, it sadly also states that efficient implementation is "nearly impossible."
Passing various enum case as function param in Swift
The linked question is actually quite close - you just have to change the parameter type to just T
, instead of T.Type
:
func printCase<T : RawRepresentable>(_ e: T) where T.RawValue == String {
print(e.rawValue)
}
Since you are accepting an instance of the enum, not the enum type itself.
Of course, you don't have to restrict this to enums where T.RawValue == String
, since print
can print Any
thing.
How can I create an instance of a generic enum in Swift
As noted, your function needs a return type if you want it to return
anything. Since you seem to want to use the function to create a value of the specified enum type, that return type should probably be either E
or E?
. (You're wrapping init?(rawValue:)
, which returns an optional because rawValue
may not map to one of the enum cases. So you either want to pass the optional through to your caller or have some logic in your function to unwrap it and handle the nil case.)
Your parameter rawValue
also needs a real type — T.Type
is not a fully qualified type in your declaration. You can get at the raw value type of the enum using the RawValue
typealias that the RawRepresentable
protocol (which you've already given as a generic constraint) defines.
So, here's your function:
func createEnum<E: RawRepresentable>(rawValue: E.RawValue) -> E? {
return E(rawValue: rawValue)
}
Note that if you try something like this:
enum Foo: Int {
case One = 1
case Two = 2
}
createEnum(1)
createEnum<Foo>(1)
It won't work — the first one doesn't specify which specialization of the generic function to use, and the second doesn't work because Swift doesn't allow manual specialization of generic functions. Instead, you have to set it up so that type inference does its thing:
let f: Foo? = createEnum(1)
someFuncThatTakesAFoo(createEnum(1))
generics type constraining for raw value in enums
If the intention is that the function accepts any enumeration type whose raw value is a string then this can be achieved with
func someFunc1<T: CaseIterable & RawRepresentable>(ofType: T.Type) where T.RawValue == String {
for aCase in T.allCases {
let someString: String = aCase.rawValue
print(someString)
}
}
someFunc1(ofType: SomeEnum.self)
// Hello
// How are you?
How to get enum value of raw type from an enum class and a string in kotlin
Here's a pure Kotlin version:
@Suppress("UNCHECKED_CAST")
fun getEnumValue(enumClass: Class<*>, value: String): Enum<*> {
val enumConstants = enumClass.enumConstants as Array<out Enum<*>>
return enumConstants.first { it.name == value }
}
Note that it's not as efficient as the Java version. java.lang.Enum.valueOf
uses a cached data structure while this version needs to create a new array to iterate over. Also this version is O(n) wheras the Java version is O(1) as it uses a dictionary under the hood.
There is an open issue in the Kotlin bug tracker to support the same code as in Java which is scheduled for 1.3.
Here's a really ugly hack to workaround the generic type issue:
private enum class Hack
fun getEnumValue(enumClass: Class<*>, value: String): Enum<*> {
return helper<Hack>(enumClass, value)
}
private fun <T : Enum<T>>helper(enumClass: Class<*>, value: String): Enum<*> {
return java.lang.Enum.valueOf(enumClass as Class<T>, value)
}
A quick test shows that it's working but I wouldn't rely on it.
If the generic type is available, you can use the built-in function enumValueOf
(see also http://kotlinlang.org/docs/reference/enum-classes.html#working-with-enum-constants):
enum class Color {
Red, Green, Blue
}
enumValueOf<Color>("Red")
Writing a generic function to get common variable from multiple enums in Swift
First of all, you need to create a CommonEnum
protocol
with ourVar
as one of the requirements like so,
protocol CommonEnum {
associatedtype T
var ourVar: T { get }
}
Now conform the above protocol
to MyEnum
and YourEnum
,
enum MyEnum: String, CaseIterable, CommonEnum {
//....
}
enum YourEnum: String,CaseIterable, CommonEnum {
//....
}
Next, the printAll(_:)
method will be
func printAll<T>(_ id: T.Type) where T: CommonEnum & RawRepresentable & CaseIterable {
for c in T.allCases {
print(c.rawValue)
print(c.ourVar)
}
}
Example:
printAll(MyEnum.self) //prints a and a1
How to write a generic function to convert any enum value to its string representation?
You need to add a constraint to the 2nd parameter : T[keyof T]
instead of number
.
Also note that the key of your enum
is of type string
.
function enumToString<T extends { [name: string]: number | string }>(myenum: T, myenumvalue: T[keyof T]) {
return myenum[myenumvalue];
}
Playground
Related Topics
How to Cancel Alamofire.Upload
Swift Combine Alternative to Rx Observable.Create
Why Avplayer Downloading First Instead Live Streaming
Swift 2 Add Protocol Conformance to Protocols
Swift: Uicollectionview Selecting Cell Indexpath Issues
How to Recognize Continuous Touch in Swift
How to Get Apple Health Data by Date Wise
Change What Print(Object) Displays in Swift 2.0
Swift: How to Fully Strip Internal/Inline Symbols
Get the Current Position of Scrollview in Swiftui
Swift Set Delegate to Self Gives Exc_Bad_Access
Swift Await/Async - How to Wait Synchronously for an Async Task to Complete
Why Do Some Types (E.G. Float80) Have a Memory Alignment Bigger Than Word Size