如何在Objective-C中使用Swift字符串枚举?

时间:2022-09-07 09:42:41

I have this enum with String values, which will be used to tell an API method that logs to a server what kind of serverity a message has. I'm using Swift 1.2, so enums can be mapped to Objective-C

我有这个带有字符串值的enum,它将用于告诉一个API方法,该方法将日志记录到服务器,消息具有哪种服务器。我使用的是Swift 1.2,所以enum可以映射到Objective-C

@objc enum LogSeverity : String {
    case Debug = "DEBUG"
    case Info = "INFO"
    case Warn = "WARN"
    case Error = "ERROR"
}

I get the error

我得到错误的

@objc enum raw type String is not an integer type

@objc enum原始类型字符串不是整数类型

I haven't managed to find anywhere which says that only integers can be translated to Objective-C from Swift. Is this the case? If so, does anyone have any best-practice suggestion on how to make something like this available in Objective-C?

我还没找到任何地方表明只有整数可以从Swift转换为Objective-C。这是这样吗?如果是这样的话,有没有人对如何在Objective-C中使用这样的工具有什么建议呢?

10 个解决方案

#1


38  

From the Xcode 6.3 release notes (emphasis added):

从Xcode 6.3发布说明中(增加重点):

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.

…现在可以使用@objc属性将Swift enums导出到Objective-C。@objc enums必须声明整数原始类型,不能是泛型或使用关联值。因为Objective-C enums没有命名空间,所以enum用例被导入到Objective-C中作为enum名称和大小写名称的连接。

#2


30  

One of the solutions is to use the RawRepresentable protocol.

其中一个解决方案是使用可表示协议。

It's not ideal to have to write the init and rawValue methods but that allows you to use this enum as usual in Swift and Objective-C.

必须编写init和rawValue方法并不理想,但这允许您像在Swift和Objective-C中一样使用这个enum。

@objc public enum LogSeverity: Int, RawRepresentable {
    case Debug
    case Info
    case Warn
    case Error

    public typealias RawValue = String

    public var rawValue: RawValue {
        switch self {
            case .Debug:
                return "DEBUG"
            case .Info:
                return "INFO"
            case .Warn:
                return "WARN"
            case .Error:
                return "ERROR"
        }
    }

    public init?(rawValue: RawValue) {
        switch rawValue {
            case "DEBUG":
                self = .Debug
            case "INFO":
                self = .Info
            case "WARN":
                self = .Warn
            case "ERROR":
                self = .Error
            default:
                self = .Debug
        }
    }
}

#3


19  

Here's a solution that works.

这是一个可行的解决方案。

@objc public enum ConnectivityStatus: Int {
    case Wifi
    case Mobile
    case Ethernet
    case Off

    func name() -> String {
        switch self {
        case .Wifi: return "wifi"
        case .Mobile: return "mobile"
        case .Ethernet: return "ethernet"
        case .Off: return "off"
        }
    }
}

#4


5  

Here is work around if you really want to achieve the goal. However, you can access the enum values in objects that Objective C accepts, not as actual enum values.

如果你真的想实现这个目标,这里有一些工作要做。但是,您可以访问Objective C接受的对象中的枚举值,而不是实际的枚举值。

enum LogSeverity : String {

    case Debug = "DEBUG"
    case Info = "INFO"
    case Warn = "WARN"
    case Error = "ERROR"

    private func string() -> String {
        return self.rawValue
    }
}

@objc
class LogSeverityBridge: NSObject {

    class func Debug() -> NSString {
        return LogSeverity.Debug.string()
    }

    class func Info() -> NSString {
        return LogSeverity.Info.string()
    }

    class func Warn() -> NSString {
        return LogSeverity.Warn.string()
    }

    class func Error() -> NSString {
        return LogSeverity.Error.string()
    }
}

To call :

电话:

NSString *debugRawValue = [LogSeverityBridge Debug]

#5


3  

Code for Xcode 8, using the fact that Int works but other methods aren't exposed to Objective-C. This is pretty horrible as it stands...

Xcode 8的代码,使用Int工作但其他方法没有暴露在Objective-C中。这实在是太可怕了……

class EnumSupport : NSObject {
    class func textFor(logSeverity severity: LogSeverity) -> String {
        return severity.text()
    }
}

@objc public enum LogSeverity: Int {
    case Debug
    case Info
    case Warn
    case Error

    func text() -> String {
        switch self {
            case .Debug: return "debug"
            case .Info: return "info"
            case .Warn: return "warn"
            case .Error: return "error"
        }
    }
}

#6


1  

Here's what I came up with. In my case, this enum was in the context providing info for a specific class, ServiceProvider.

这是我想到的。在我的例子中,这个enum是为特定的类,ServiceProvider提供信息的。

class ServiceProvider {
    @objc enum FieldName : Int {
        case CITY
        case LATITUDE
        case LONGITUDE
        case NAME
        case GRADE
        case POSTAL_CODE
        case STATE
        case REVIEW_COUNT
        case COORDINATES

        var string: String {
            return ServiceProvider.FieldNameToString(self)
        }
    }

    class func FieldNameToString(fieldName:FieldName) -> String {
        switch fieldName {
        case .CITY:         return "city"
        case .LATITUDE:     return "latitude"
        case .LONGITUDE:    return "longitude"
        case .NAME:         return "name"
        case .GRADE:        return "overallGrade"
        case .POSTAL_CODE:  return "postalCode"
        case .STATE:        return "state"
        case .REVIEW_COUNT: return "reviewCount"
        case .COORDINATES:  return "coordinates"
        }
    }
}

From Swift, you can use .string on an enum (similar to .rawValue). From Objective-C, you can use [ServiceProvider FieldNameToString:enumValue];

从Swift开始,您可以在枚举中使用.string(类似于. rawvalue)。从Objective-C中,可以使用[ServiceProvider FieldNameToString:enumValue];

#7


1  

You can create an private Inner enum. The implementation is a bit repeatable, but clear and easy. 1 line rawValue, 2 lines init, which always look the same. The Inner has a method returning the "outer" equivalent, and vice-versa.

您可以创建一个私有的内部枚举。实现是有点可重复的,但是很清晰和容易。一行rawValue,两行init,它们总是相同的。内部具有返回“外部”等效项的方法,反之亦然。

Has the added benefit that you can directly map the enum case to a String, unlike other answers here.

具有附加的好处,您可以直接将enum案例映射到字符串,与这里的其他答案不同。

Please feel welcome to build on this answer if you know how to solve the repeatability problem with templates, I don't have time to mingle with it right now.

如果您知道如何使用模板解决重复性问题,那么欢迎您在此基础上构建这个答案,我现在没有时间与它混合。

@objc enum MyEnum: NSInteger, RawRepresentable, Equatable {
    case
    option1,
    option2,
    option3

    // MARK: RawRepresentable

    var rawValue: String {
        return toInner().rawValue
    }

    init?(rawValue: String) {
        guard let value = Inner(rawValue: rawValue)?.toOuter() else { return nil }
        self = value
    }

    // MARK: Obj-C support

    private func toInner() -> Inner {
        switch self {
        case .option1: return .option1
        case .option3: return .option3
        case .option2: return .option2
        }
    }

    private enum Inner: String {
        case
        option1 = "option_1",
        option2 = "option_2",
        option3 = "option_3"

        func toOuter() -> MyEnum {
            switch self {
            case .option1: return .option1
            case .option3: return .option3
            case .option2: return .option2
            }
        }
    }
}

#8


1  

If you don't mind to define the values in (Objective) C, you can use the NS_TYPED_ENUM macro to import constants in Swift.

如果您不介意在(Objective) C中定义值,那么可以使用NS_TYPED_ENUM宏来快速导入常量。

For example:

例如:

.h file

. h文件

typedef NSString *const ProgrammingLanguage NS_TYPED_ENUM;

FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageSwift;
FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageObjectiveC;

.m file

m文件

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

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

在Swift中,这是作为一个结构导入的:

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.

尽管该类型不是作为枚举被桥接的,但它在Swift代码中使用时感觉非常类似。

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

在使用Cocoa和Objective-C文档中使用Swift的“与C api交互”中,您可以阅读更多有关此技术的信息。

#9


0  

I had a use case where I still needed to associate an string value in the Swift side. I wrote about my solution here: https://medium.com/@oscarcortes/using-swift-string-enums-in-objective-c-f6683da5b92e Essentially, you can add a method in the Swift enum that returns a string based on the current enum value.

我有一个用例,我仍然需要在Swift端关联一个字符串值。我在这里写了我的解决方案:https://medium.com/@oscarcortes/using-斯威夫特-string-enums-in-objective-c-f6683da5b92e本质上,您可以在Swift enum中添加一个方法,该方法根据当前enum值返回一个字符串。

#10


0  

This is my use case:

这是我的用例:

  • I avoid hard-coded Strings whenever I can, so that I get compile warnings when I change something
  • 我尽可能地避免硬编码字符串,这样当我更改某些内容时就会得到编译警告
  • I have a fixed list of String values coming from a back end, which can also be nil
  • 我有一个固定的字符串值列表来自后端,它也可以是nil

Here's my solution that involves no hard-coded Strings at all, supports missing values, and can be used elegantly in both Swift and Obj-C:

以下是我的解决方案,它完全不涉及硬编码字符串,支持丢失的值,并且可以在Swift和object -c中优雅地使用:

@objc enum InventoryItemType: Int {
    private enum StringInventoryItemType: String {
        case vial
        case syringe
        case crystalloid
        case bloodProduct
        case supplies
    }

    case vial
    case syringe
    case crystalloid
    case bloodProduct
    case supplies
    case unknown

    static func fromString(_ string: String?) -> InventoryItemType {
        guard let string = string else {
            return .unknown
        }
        guard let stringType = StringInventoryItemType(rawValue: string) else {
            return .unknown
        }
        switch stringType {
        case .vial:
            return .vial
        case .syringe:
            return .syringe
        case .crystalloid:
            return .crystalloid
        case .bloodProduct:
            return .bloodProduct
        case .supplies:
            return .supplies
        }
    }

    var stringValue: String? {
        switch self {
        case .vial:
            return StringInventoryItemType.vial.rawValue
        case .syringe:
            return StringInventoryItemType.syringe.rawValue
        case .crystalloid:
            return StringInventoryItemType.crystalloid.rawValue
        case .bloodProduct:
            return StringInventoryItemType.bloodProduct.rawValue
        case .supplies:
            return StringInventoryItemType.supplies.rawValue
        case .unknown:
            return nil
        }
    }
}

#1


38  

From the Xcode 6.3 release notes (emphasis added):

从Xcode 6.3发布说明中(增加重点):

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.

…现在可以使用@objc属性将Swift enums导出到Objective-C。@objc enums必须声明整数原始类型,不能是泛型或使用关联值。因为Objective-C enums没有命名空间,所以enum用例被导入到Objective-C中作为enum名称和大小写名称的连接。

#2


30  

One of the solutions is to use the RawRepresentable protocol.

其中一个解决方案是使用可表示协议。

It's not ideal to have to write the init and rawValue methods but that allows you to use this enum as usual in Swift and Objective-C.

必须编写init和rawValue方法并不理想,但这允许您像在Swift和Objective-C中一样使用这个enum。

@objc public enum LogSeverity: Int, RawRepresentable {
    case Debug
    case Info
    case Warn
    case Error

    public typealias RawValue = String

    public var rawValue: RawValue {
        switch self {
            case .Debug:
                return "DEBUG"
            case .Info:
                return "INFO"
            case .Warn:
                return "WARN"
            case .Error:
                return "ERROR"
        }
    }

    public init?(rawValue: RawValue) {
        switch rawValue {
            case "DEBUG":
                self = .Debug
            case "INFO":
                self = .Info
            case "WARN":
                self = .Warn
            case "ERROR":
                self = .Error
            default:
                self = .Debug
        }
    }
}

#3


19  

Here's a solution that works.

这是一个可行的解决方案。

@objc public enum ConnectivityStatus: Int {
    case Wifi
    case Mobile
    case Ethernet
    case Off

    func name() -> String {
        switch self {
        case .Wifi: return "wifi"
        case .Mobile: return "mobile"
        case .Ethernet: return "ethernet"
        case .Off: return "off"
        }
    }
}

#4


5  

Here is work around if you really want to achieve the goal. However, you can access the enum values in objects that Objective C accepts, not as actual enum values.

如果你真的想实现这个目标,这里有一些工作要做。但是,您可以访问Objective C接受的对象中的枚举值,而不是实际的枚举值。

enum LogSeverity : String {

    case Debug = "DEBUG"
    case Info = "INFO"
    case Warn = "WARN"
    case Error = "ERROR"

    private func string() -> String {
        return self.rawValue
    }
}

@objc
class LogSeverityBridge: NSObject {

    class func Debug() -> NSString {
        return LogSeverity.Debug.string()
    }

    class func Info() -> NSString {
        return LogSeverity.Info.string()
    }

    class func Warn() -> NSString {
        return LogSeverity.Warn.string()
    }

    class func Error() -> NSString {
        return LogSeverity.Error.string()
    }
}

To call :

电话:

NSString *debugRawValue = [LogSeverityBridge Debug]

#5


3  

Code for Xcode 8, using the fact that Int works but other methods aren't exposed to Objective-C. This is pretty horrible as it stands...

Xcode 8的代码,使用Int工作但其他方法没有暴露在Objective-C中。这实在是太可怕了……

class EnumSupport : NSObject {
    class func textFor(logSeverity severity: LogSeverity) -> String {
        return severity.text()
    }
}

@objc public enum LogSeverity: Int {
    case Debug
    case Info
    case Warn
    case Error

    func text() -> String {
        switch self {
            case .Debug: return "debug"
            case .Info: return "info"
            case .Warn: return "warn"
            case .Error: return "error"
        }
    }
}

#6


1  

Here's what I came up with. In my case, this enum was in the context providing info for a specific class, ServiceProvider.

这是我想到的。在我的例子中,这个enum是为特定的类,ServiceProvider提供信息的。

class ServiceProvider {
    @objc enum FieldName : Int {
        case CITY
        case LATITUDE
        case LONGITUDE
        case NAME
        case GRADE
        case POSTAL_CODE
        case STATE
        case REVIEW_COUNT
        case COORDINATES

        var string: String {
            return ServiceProvider.FieldNameToString(self)
        }
    }

    class func FieldNameToString(fieldName:FieldName) -> String {
        switch fieldName {
        case .CITY:         return "city"
        case .LATITUDE:     return "latitude"
        case .LONGITUDE:    return "longitude"
        case .NAME:         return "name"
        case .GRADE:        return "overallGrade"
        case .POSTAL_CODE:  return "postalCode"
        case .STATE:        return "state"
        case .REVIEW_COUNT: return "reviewCount"
        case .COORDINATES:  return "coordinates"
        }
    }
}

From Swift, you can use .string on an enum (similar to .rawValue). From Objective-C, you can use [ServiceProvider FieldNameToString:enumValue];

从Swift开始,您可以在枚举中使用.string(类似于. rawvalue)。从Objective-C中,可以使用[ServiceProvider FieldNameToString:enumValue];

#7


1  

You can create an private Inner enum. The implementation is a bit repeatable, but clear and easy. 1 line rawValue, 2 lines init, which always look the same. The Inner has a method returning the "outer" equivalent, and vice-versa.

您可以创建一个私有的内部枚举。实现是有点可重复的,但是很清晰和容易。一行rawValue,两行init,它们总是相同的。内部具有返回“外部”等效项的方法,反之亦然。

Has the added benefit that you can directly map the enum case to a String, unlike other answers here.

具有附加的好处,您可以直接将enum案例映射到字符串,与这里的其他答案不同。

Please feel welcome to build on this answer if you know how to solve the repeatability problem with templates, I don't have time to mingle with it right now.

如果您知道如何使用模板解决重复性问题,那么欢迎您在此基础上构建这个答案,我现在没有时间与它混合。

@objc enum MyEnum: NSInteger, RawRepresentable, Equatable {
    case
    option1,
    option2,
    option3

    // MARK: RawRepresentable

    var rawValue: String {
        return toInner().rawValue
    }

    init?(rawValue: String) {
        guard let value = Inner(rawValue: rawValue)?.toOuter() else { return nil }
        self = value
    }

    // MARK: Obj-C support

    private func toInner() -> Inner {
        switch self {
        case .option1: return .option1
        case .option3: return .option3
        case .option2: return .option2
        }
    }

    private enum Inner: String {
        case
        option1 = "option_1",
        option2 = "option_2",
        option3 = "option_3"

        func toOuter() -> MyEnum {
            switch self {
            case .option1: return .option1
            case .option3: return .option3
            case .option2: return .option2
            }
        }
    }
}

#8


1  

If you don't mind to define the values in (Objective) C, you can use the NS_TYPED_ENUM macro to import constants in Swift.

如果您不介意在(Objective) C中定义值,那么可以使用NS_TYPED_ENUM宏来快速导入常量。

For example:

例如:

.h file

. h文件

typedef NSString *const ProgrammingLanguage NS_TYPED_ENUM;

FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageSwift;
FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageObjectiveC;

.m file

m文件

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

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

在Swift中,这是作为一个结构导入的:

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.

尽管该类型不是作为枚举被桥接的,但它在Swift代码中使用时感觉非常类似。

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

在使用Cocoa和Objective-C文档中使用Swift的“与C api交互”中,您可以阅读更多有关此技术的信息。

#9


0  

I had a use case where I still needed to associate an string value in the Swift side. I wrote about my solution here: https://medium.com/@oscarcortes/using-swift-string-enums-in-objective-c-f6683da5b92e Essentially, you can add a method in the Swift enum that returns a string based on the current enum value.

我有一个用例,我仍然需要在Swift端关联一个字符串值。我在这里写了我的解决方案:https://medium.com/@oscarcortes/using-斯威夫特-string-enums-in-objective-c-f6683da5b92e本质上,您可以在Swift enum中添加一个方法,该方法根据当前enum值返回一个字符串。

#10


0  

This is my use case:

这是我的用例:

  • I avoid hard-coded Strings whenever I can, so that I get compile warnings when I change something
  • 我尽可能地避免硬编码字符串,这样当我更改某些内容时就会得到编译警告
  • I have a fixed list of String values coming from a back end, which can also be nil
  • 我有一个固定的字符串值列表来自后端,它也可以是nil

Here's my solution that involves no hard-coded Strings at all, supports missing values, and can be used elegantly in both Swift and Obj-C:

以下是我的解决方案,它完全不涉及硬编码字符串,支持丢失的值,并且可以在Swift和object -c中优雅地使用:

@objc enum InventoryItemType: Int {
    private enum StringInventoryItemType: String {
        case vial
        case syringe
        case crystalloid
        case bloodProduct
        case supplies
    }

    case vial
    case syringe
    case crystalloid
    case bloodProduct
    case supplies
    case unknown

    static func fromString(_ string: String?) -> InventoryItemType {
        guard let string = string else {
            return .unknown
        }
        guard let stringType = StringInventoryItemType(rawValue: string) else {
            return .unknown
        }
        switch stringType {
        case .vial:
            return .vial
        case .syringe:
            return .syringe
        case .crystalloid:
            return .crystalloid
        case .bloodProduct:
            return .bloodProduct
        case .supplies:
            return .supplies
        }
    }

    var stringValue: String? {
        switch self {
        case .vial:
            return StringInventoryItemType.vial.rawValue
        case .syringe:
            return StringInventoryItemType.syringe.rawValue
        case .crystalloid:
            return StringInventoryItemType.crystalloid.rawValue
        case .bloodProduct:
            return StringInventoryItemType.bloodProduct.rawValue
        case .supplies:
            return StringInventoryItemType.supplies.rawValue
        case .unknown:
            return nil
        }
    }
}