如何在 Objective-C 中使用 Swift String 枚举?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30480338/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 21:34:38  来源:igfitidea点击:

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

objective-cswiftenumsinterop

提问by bogen

I have this enum with Stringvalues, 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

我有这个带有String值的枚举,它将用于告诉将消息记录到服务器的 API 方法。我使用的是 Swift 1.2,所以枚举可以映射到 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 原始类型 String 不是整数类型

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 中提供此类内容有任何最佳实践建议?

采纳答案by Martin R

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.

Swift 语言增强功能

...
现在可以使用 @objc 属性将 Swift 枚举导出到 Objective-C。@objc 枚举必须声明整数原始类型,并且不能是泛型或使用关联值。因为 Objective-C 枚举没有命名空间,所以枚举 case 被导入到 Objective-C 作为枚举名称和 case 名称的串联。

回答by Remy Cilia

One of the solutions is to use the RawRepresentableprotocol.

解决方案之一是使用RawRepresentable协议。

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

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

@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:
                return nil
        }
    }
}

回答by David

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"
        }
    }
}

回答by BLC

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]

回答by Dan Rosenstark

Code for Xcode 8, using the fact that Intworks 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"
        }
    }
}

回答by RvdB

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

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

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 structas such:

在 Swift 中,它是这样导入的struct

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.

虽然该类型没有被桥接为enum,但在 Swift 代码中使用它时感觉与 1 非常相似。

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

您可以在Using Swift with Cocoa and Objective-C 文档的“Interacting with C APIs”中阅读有关此技术的更多信息

回答by Chris Garrett

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 和 Obj-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
        }
    }
}

回答by Jafar Khoshtabiat

I think @Remi 's answer crashes in some situations as I had this:

我认为 @Remi 的回答在某些情况下会崩溃,因为我有这个:

My error's screesshot. so I post my edition for @Remi 's answer:

我的错误的 screesshot。所以我为@Remi 的回答发布了我的版本:

@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:
                return nil
        }
    }
}

回答by Pawel Jurczyk

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

您可以创建一个私有Inner枚举。实现有点可重复,但清晰易行。1 行rawValue,2 行init,看起来总是一样的。所述Inner具有返回“外”等效的方法,并且反之亦然。

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

String与此处的其他答案不同,您可以将枚举案例直接映射到 a 的额外好处。

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
            }
        }
    }
}

回答by Chris Prince

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

这是我想出的。在我的例子中,这个枚举是在为特定类提供信息的上下文中,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 .stringon an enum (similar to .rawValue). From Objective-C, you can use [ServiceProvider FieldNameToString:enumValue];

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