Get name of enum case with associated data

I have an associated value enum:

enum Message {
   case simple(String)
}

How do I print the name Message.simple?

enum Message {
    case simple(String)
}

let message = Message.simple("Hello")
let m = Mirror(reflecting: message).children.first!
print("\(type(of: message)).\(m.label!)(\"\(m.value)\")")
// prints: Message.simple("Hello")
2 Likes

A generalised version that supports cases with no associated values.

protocol HasEnumCaseName {
    var caseName: String { get }
}

extension HasEnumCaseName {
    var caseName: String {
        Mirror(reflecting: self).children.first?.label ?? "\(self)"
    }
}

// example:

enum Message: HasEnumCaseName {
    case simple(String)
    case noAssocValue
}

print(Message.simple("Hello").caseName) // simple
print(Message.noAssocValue.caseName) // noAssocValue

if you need the type as well – add it separately with type(of: xxx).

3 Likes

Those answers are good, but assume that you have an instance of Message.

Do you?

Because the simplest answer to your question is

print("Message.simple")

Otherwise, without using a protocol, you can filter non-enums. subjectType is available too.

extension String {
  init?(droppingAssociatedValue enum: some Any) {
    let mirror = Mirror(reflecting: `enum`)
    guard mirror.displayStyle == .enum else { return nil }
    self = """
      \(mirror.subjectType).\
      \(mirror.children.first?.label ?? "\(`enum`)")
      """
  }
}
2 Likes

Actually, I don't... :smile:

Here's what's happening, I have messages of different types say:

enum Message {
   case simple(String)
   case  other(String)
}

And I'm doing some unit tests like this:

//arrange 
let message = "...."
//act
let parser = Parser()

let message: Message = parser.parser()
/// assert
guard case .simple(_)  = message else {
            XCTFail("Message is of type \(type(of: message)) when it should have been of type \("???")")                // <--- Here's the "thing"
}

I have no idea if that's possible without two instances.

guard case .simple = message else {
  return XCTFail(
    nonMatchingEnumCaseMessage(message, exampleMatch: .simple(""))
  )
}

XCTest's standard formatting is like this, though:

extension XCTestCase {
  func nonMatchingEnumCaseMessage<Enum>(
    _ nonMatch: Enum,
    exampleMatch: Enum
  ) -> String {
    func caseLabel(_ instance: Enum) -> String {
      Mirror(reflecting: instance).children.first?.label ?? "\(instance)"
    }

    return """
      \(Enum.self) is case \
      ("\(caseLabel(nonMatch))") \
      when it should have been \
      ("\(caseLabel(exampleMatch))")
      """
  }
}