Unfortunately, at the moment this is not like Swift, but I thought about your problem for a while, and I suggest 3 ways that the Swift team could allow you to solve this problem.
Fix mirror for listings. The simplest solution is the one that I'm sure you have already tried. You are trying to create a reflection library, and you would like to reflect the value of Any to see if this is an enumeration, and if so, you want to see if it has the original value. The rawValue property must be available through 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 does not work. You will find that the mirror has count of 0 . I really think this is the oversight of the Swift team in their implementation of Swift._EnumMirror , and I will be presenting a radar about it. rawValue definitely a legal property. This is a strange scenario, because enumerations are not allowed to have other stored properties. In addition, your enumeration declaration never explicitly matches RawRepresentable , and does not declare a rawValue property. The compiler simply indicates that when you enter enum MyEnum: String or : Int or something else. In my tests, it seems that it does not matter if the property is defined in the protocol or is an instance of a related type, it should still be the property represented in the mirror.
Allow protocol types with specific related types. As I mentioned in my comment above, this is a limitation in Swift that cannot be applied to a protocol type associated with type requirements. You cannot just use RawRepresentable because Swift does not know which type will return the rawValue property. Syntax such as RawRepresentable<where RawValue == String> is possible, or perhaps protocol<RawRepresentable where RawValue == String> . If that were possible, you could try applying to the type through a switch statement, as in:
switch theEnum { case let rawEnum as protocol<RawRepresentable where RawValue == String>: return rawEnum.rawValue
But this is not defined in Swift. And if you just try to use RawRepresentable , the Swift compiler tells you that you can only use it in a common function, but looking at your code only leads you to a rabbit hole. Common functions require type information at compile time in order to work, and what exactly you do not work with Any instances.
The Swift team can modify protocols to look more like generic classes and structures. For example, MyGenericStruct<MyType> and MyGenericClass<MyType> are legally specialized specific types that you can execute and perform runtime checks. However, the Swift team may have good reason not to want to do this using protocols. Specialized versions of protocols (i.e. protocol references with known associated types) will still not be specific types. I would not hold my breath with this ability. I consider this the weakest of the solutions we have proposed.
Extend protocols to match protocols. I really thought I could make this solution work for you, but alas, no. Since Swift's built-in mirror for enumerations does not apply to rawValue , I thought, why not implement my own custom 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) } }
Fine! Now all we need to do is expand RawRepresentable to Reflectable so that we can first cast theEnum as Reflectable (no related type is required) and then call reflect(theEnum) to give us our amazing custom mirror:
extension RawRepresentable: Reflectable { func getMirror() -> MirrorType { return RawRepresentableMirror(self) } }
Compiler error: protocol extension "RawRepresentable" cannot be an offer of inheritance
Whaaaat ?! We can expand specific types to implement new protocols, such as:
extension MyClass: MyProtocol { // Conform to new protocol }
With Swift 2, we can extend the protocols to give specific implementations of functions, such as:
extension MyProtocol { // Default implementations for MyProtocol }
I thought we could extend the protocols to implement other protocols, but apparently not! I see no reason why we could not get Swift to do this. I think that would fit very well into the protocol-oriented paradigm mentioned in WWDC 2015. Specific types that implement the original protocol would receive free new protocol compatibility, but a particular type can also define its own versions of the new protocol methods. I will definitely write a request for improvement, because I think it can be a powerful feature. In fact, I think this can be very useful in your reflection library.
Edit: Returning to my dissatisfaction with answer 2, I think there is a more elegant and realistic opportunity to work with these protocols in general, and this actually combines my idea with answer 3 about expanding protocols to comply with new protocols. The idea is for protocols with related types to conform to new protocols that extract the erasable 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 will not extend inheritance as such. Most likely, this simply tells the compiler: "No matter where the type matching RawRepresentable , this type also matches AnyRawRepresentable with this default implementation." AnyRawRepresentable will not have related type requirements, but it can still get rawValue like Any . In our code:
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 type of solution can be widely used with any type of protocol with the corresponding types. I will also include this idea in my proposal to the Swift protocol-wide protocol extension team.
Refresh . None of the above options are available as of Swift 4. I have not received an answer about why the Mirror in the RawRepresentable enumeration does not contain its rawValue . As for options No. 2 and No. 3, they are still within the scope for future releases of Swift . They were mentioned on the Swift mailing list and in the Manifold Manifesto document created by Doug Gregor of the Swift team.
The correct term for option # 2 ("Allow protocol types with certain related types") is a generalized existence . This would allow, possibly, to automatically transfer protocols with associated Any types, where there is a related type, or to allow syntax as follows:
anyEnum as? Any<RawRepresentable where .RawValue == String>
Option # 3 is mentioned on the mailing list, and this is usually a rejected feature requested, but this does not mean that it cannot be included in future versions of Swift. In the Generics manifest, it is called Conditional Compliance through Protocol Extensions . Recognizing its strength as a feature, unfortunately, it is also said that effective implementation is "almost impossible."