Getting String Name of Objective-C @Objc Enum Value in Swift

How to make a Swift String enum available in Objective-C?

From the Xcode 6.3 release notes (emphasis added):

Swift Language Enhancements

...

Swift enums can now be exported to Objective-C using the @objc
attribute. @objc enums must declare an integer raw type, and cannot be
generic or use associated values. Because Objective-C enums are not
namespaced, enum cases are imported into Objective-C as the
concatenation of the enum name and case name.

How to make a Swift enum available in Objective-C?

You can use only Enums witch can be represented in objc:

  1. Enums without nested enums
  2. Enums without parameters
  3. Enums raw types
    should be integer

Therefore you can only create another enum, witch can be represented in objc and just add method to convert to it:

public enum TrackingValue {
...

func toObjc() -> ObjcEnum {
...
}
}

and create somewhere swift method to another side:

func convert(_ type: ObjcEnum) -> TrackingValue {
...
}

How to get the name of enumeration value in Swift?

As of Xcode 7 beta 5 (Swift version 2) you can now print type names and enum cases by default using print(_:), or convert to String using String's init(_:) initializer or string interpolation syntax. So for your example:

enum City: Int {
case Melbourne = 1, Chelyabinsk, Bursa
}
let city = City.Melbourne

print(city)
// prints "Melbourne"

let cityName = "\(city)" // or `let cityName = String(city)`
// cityName contains "Melbourne"

So there is no longer a need to define & maintain a convenience function that switches on each case to return a string literal. In addition, this works automatically for any enum, even if no raw-value type is specified.

debugPrint(_:) & String(reflecting:) can be used for a fully-qualified name:

debugPrint(city)
// prints "App.City.Melbourne" (or similar, depending on the full scope)

let cityDebugName = String(reflecting: city)
// cityDebugName contains "App.City.Melbourne"

Note that you can customise what is printed in each of these scenarios:

extension City: CustomStringConvertible {
var description: String {
return "City \(rawValue)"
}
}

print(city)
// prints "City 1"

extension City: CustomDebugStringConvertible {
var debugDescription: String {
return "City (rawValue: \(rawValue))"
}
}

debugPrint(city)
// prints "City (rawValue: 1)"

(I haven't found a way to call into this "default" value, for example, to print "The city is Melbourne" without resorting back to a switch statement. Using \(self) in the implementation of description/debugDescription causes an infinite recursion.)



The comments above String's init(_:) & init(reflecting:) initializers describe exactly what is printed, depending on what the reflected type conforms to:

extension String {
/// Initialize `self` with the textual representation of `instance`.
///
/// * If `T` conforms to `Streamable`, the result is obtained by
/// calling `instance.writeTo(s)` on an empty string s.
/// * Otherwise, if `T` conforms to `CustomStringConvertible`, the
/// result is `instance`'s `description`
/// * Otherwise, if `T` conforms to `CustomDebugStringConvertible`,
/// the result is `instance`'s `debugDescription`
/// * Otherwise, an unspecified result is supplied automatically by
/// the Swift standard library.
///
/// - SeeAlso: `String.init<T>(reflecting: T)`
public init<T>(_ instance: T)

/// Initialize `self` with a detailed textual representation of
/// `subject`, suitable for debugging.
///
/// * If `T` conforms to `CustomDebugStringConvertible`, the result
/// is `subject`'s `debugDescription`.
///
/// * Otherwise, if `T` conforms to `CustomStringConvertible`, the result
/// is `subject`'s `description`.
///
/// * Otherwise, if `T` conforms to `Streamable`, the result is
/// obtained by calling `subject.writeTo(s)` on an empty string s.
///
/// * Otherwise, an unspecified result is supplied automatically by
/// the Swift standard library.
///
/// - SeeAlso: `String.init<T>(T)`
public init<T>(reflecting subject: T)
}



See the release notes for info about this change.

Is it possible to use Swift's Enum in Obj-C?

As of Swift version 1.2 (Xcode 6.3) you can. Simply prefix the enum declaration with @objc

@objc enum Bear: Int {
case Black, Grizzly, Polar
}

Shamelessly taken from the Swift Blog

Note: This would not work for String enums or enums with associated values. Your enum will need to be Int-bound


In Objective-C this would look like

Bear type = BearBlack;
switch (type) {
case BearBlack:
case BearGrizzly:
case BearPolar:
[self runLikeHell];
}

Objc visible string enum but not RawRepresentable

Swift Language Enhancements

... Swift enums can now be exported to Objective-C using the @objc
attribute. @objc enums must declare an integer raw type, and cannot be
generic or use associated values. Because Objective-C enums are not
namespaced, enum cases are imported into Objective-C as the
concatenation of the enum name and case name.

Above From Xcode 6.4 Release Notes


For this purpose you define the values in Objective-C, you can use the NS_TYPED_ENUM macro to import constants in Swift
For example:
.h file

typedef NSString *const ProgrammingLanguage NS_TYPED_ENUM;

FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageSwift;
FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageObjectiveC;

.m file

ProgrammingLanguage ProgrammingLanguageSwift = @"Swift";
ProgrammingLanguage ProgrammingLanguageObjectiveC = @"ObjectiveC";

In Swift, this is imported as a struct as such:

struct ProgrammingLanguage: RawRepresentable, Equatable, Hashable {
typealias RawValue = String

init(rawValue: RawValue)
var rawValue: RawValue { get }

static var swift: ProgrammingLanguage { get }
static var objectiveC: ProgrammingLanguage { get }
}

Although the type is not bridged as an enum, it feels very similar to one when using it in Swift code.

You can read more about this technique in the "Interacting with C APIs" of the Using Swift with Cocoa and Objective-C documentation

Objective-C enum in Swift

These get translated to

countDirection.Up
countDirection.Count

Swift removes as many letters as possible that the enum values have in common with the enumeration name. In your case, with an enumeration called countDirection and a value countDirectionUp, the whole "countDirection" is removed. It's not needed because you know which enum you are using, making your code considerable shorter.

Using Swift Class and Enum from Objective-C

Isn't the enum implicitly Int?

Not really. Objective-C cannot see a Swift enum at all. In Swift, an enum is an object type. Objective-C has no knowledge whatever of any such object type; its only objects are classes. (In Objective-C, enums are just numbers with names.) Therefore, neither a Swift enum type, nor a method that takes or produces a Swift enum, nor a Swift enum property, is exposed to Objective-C

However, in the special case where you say @objc enum BannerStyle: Int, it is translated into an Objective-C enum for you. So, in Objective-C, names such as BannerStyleDanger and BannerStyleInfo will spring to life. But they will just be integers.

enum Values to NSString (iOS)

This is answered here: a few suggestions on implementation

The bottom line is Objective-C is using a regular, old C enum, which is just a glorified set of integers.

Given an enum like this:

typedef enum { a, b, c } FirstThreeAlpha;

Your method would look like this:

- (NSString*) convertToString:(FirstThreeAlpha) whichAlpha {
NSString *result = nil;

switch(whichAlpha) {
case a:
result = @"a";
break;
case b:
result = @"b";
break;
case c:
result = @"c";
break;

default:
result = @"unknown";
}

return result;
}


Related Topics



Leave a reply



Submit