首页 文章

如何在Objective-C中提供Swift String枚举?

提问于
浏览
44

我有这个带有 String 值的枚举,它将用于告诉API方法,该方法会向服务器记录消息所具有的服务器类型 . 我正在使用Swift 1.2,因此枚举可以映射到Objective-C

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

我收到了错误

@objc enum raw type String不是整数类型

我没有设法找到任何说只有整数可以从Swift转换为Objective-C的地方 . 是这样的吗?如果是这样,有没有人有任何关于如何在Objective-C中提供这样的东西的最佳实践建议?

9 回答

  • 3

    来自Xcode 6.3 release notes(重点补充):

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

  • 42

    其中一个解决方案是使用RawRepresentable协议 .

    编写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:
                    self = .Debug
            }
        }
    }
    
  • 39

    这是一个有效的解决方案 .

    @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"
            }
        }
    }
    
  • 1

    如果你真的想实现这个目标,这就是解决方法 . 但是,您可以访问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()
        }
    }
    

    致电:

    NSString *debugRawValue = [LogSeverityBridge Debug]
    
  • 1

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

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

    例如:

    .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";
    

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

    尽管该类型没有被桥接为 enum ,但在Swift代码中使用它时感觉非常类似 .

    您可以在Using Swift with Cocoa and Objective-C documentation的"Interacting with C APIs"中阅读有关此技术的更多信息 .

  • 20

    这就是我想出来的 . 在我的例子中,这个枚举是在上下文中提供特定类的信息, 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"
            }
        }
    }
    

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

  • 2

    您可以创建私有 Inner 枚举 . 实现有点可重复,但清晰简单 . 1行 rawValue ,2行 init ,总是看起来一样 . Inner 有一个返回"outer"等效的方法,反之亦然 .

    有一个额外的好处,你可以直接将枚举案例映射到 String ,这与其他答案不同 .

    如果您知道如何使用模板解决重复性问题,请立即 Build 此答案,我现在没有时间与它混在一起 .

    @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
                }
            }
        }
    }
    
  • 5

    这是我的用例:

    • 我尽可能避免使用硬编码的字符串,这样当我改变某些内容时我会收到编译警告

    • 我有一个来自后端的固定String值列表,也可以是nil

    这是我的解决方案,根本不涉及硬编码字符串,支持缺失值,并且可以在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
            }
        }
    }
    

相关问题