Using Swift String enums in Objective-C

Enumerations are a really handy tool in Objective-C and in Swift, enums provide a lot more features. The case for using enumerations is clear, you want to define a readable way to name options that you can use throughout your code instead of string constants or plain numbers.

In my case I recently ran into a situation where I wanted to refactor some Objective-C code that deals with analytics tracking but I wanted to use Swift for that. These were my requirements:

  • I wanted to use an enum to track named events. For example. I wanted to be able to simply type :
      [myAnalyticsManager trackEvent:AppEvent.MyButtonClicked]
  • But also be able to associate a string value to each enum as the analytics SDK takes a string as a event name.
  • In addition I needed this Swift code to be available to the rest of the existing Objective-C classes doing analytics.

What I learned

Event though Swift supports string enums they cannot be used in Objective-C, only Int enums are allowed.

If I was going to use Swift everywhere, this would be an easy problem to solve as Swift supports string enums, so I could’ve declared something like:

enum AppEvent:String {
case ButtonClicked: "ButtonClicked"
case OtherButtonClicked: "OtherClicked"
}

And then use the rawValue when calling the third party analytics API.

I also looked into using Structs which could be initialized with an int and which then I’d initialize internally with the desired string. This was not a perfect solution since I still needed a parallel Objective-C enum to match the Struct values but at least it was a good intermediate bridge with Swift. But all this didn’t matter because Structs cannot be used in Objective-C either!

The Solution

So it turns out that you can declare methods within an enum and use the enum values to return whatever you want. So this is how my enum ended up:

@objc enum AppEvent:Int {
case:ButtonClicked
case:OtherButtonClicked

function name () -> String {
switch self {
case ButtonClicked: return "ButtonClicked" case OtherButtonClicked: return "OtherButtonClicked"
}
}
}

And then within the Swift class calling the third party analytics API:

func trackEvent( appEvent:AppEvent) {
someAnalyticsAPI.track(appEvent.name())
}

And then a calling from Objective-C

[myAnalyticsManager trackEvent:AppEventMyButtonClicked]

I was happy with this solution all the new code will be in Swift but still compatible with the existing Objective-C code. It is also scalable, since more methods could be added to the Swift enum to extend it(e.g if parameters need to be sent to the analytics API). In addition, if a future version of Swift supports using String enums in Objective-C, this could be easily updated.

Happy Swift coding!