【问题标题】:How to make a Swift String enum available in Objective-C?如何在 Objective-C 中使用 Swift 字符串枚举?
【发布时间】:2015-08-09 09:55:32
【问题描述】:

我有这个带有 String 值的枚举,它将用于告诉 API 方法记录到服务器的消息具有什么样的服务性。我使用的是 Swift 1.2,所以枚举可以映射到 Objective-C

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

我得到了错误

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

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

【问题讨论】:

    标签: objective-c swift enums interop


    【解决方案1】:

    其中一种解决方案是使用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:
                    return nil
            }
        }
    }
    

    【讨论】:

    • 我真的很喜欢这种方法。为了使其完美,可以通过定义[LogSeverity: String] 类型的字典来避免一些代码重复,然后可以在一行中定义rawValueinit? 方法。
    • @Gobe 你能分享一下如何编写 rawValue 和 init 的例子吗?请把方法放在一行中好吗?
    • @DanielSanchez 如果你有一个 LogSeverity 类型的枚举,它可以由 Strings 原始表示,并且你定义了一次 [LogSeverity: String] 类型的字典,那么 rawValue 就是 myDictionary[self]初始化是self = myDictionary.first(where: { $0.value == rawValue })
    • 这似乎是一个不错的答案,但我在尝试后遇到了奇怪的 bad_access 崩溃。
    • @VladimirsMatusevics 我修复了它并请求编辑,tnx 进行更正
    【解决方案2】:

    来自Xcode 6.3 release notes(已添加重点):

    Swift 语言增强

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

    【讨论】:

    • 链接上找不到页面
    【解决方案3】:

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

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

    【讨论】:

    • 在Objective-C中如何调用name()函数?
    • @David 但你不能在 objc 中调用 name()
    • @Chuck 即使是公共函数也不会暴露方法。
    • 不知道为什么这个答案被投票了,这不能从 Obj-C 访问
    • 是的,我也不能从 objc 调用
    【解决方案4】:

    如果您真的想实现目标,这里有一些解决方法。但是,您可以访问 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]
    

    【讨论】:

    • 我看到的问题是你不能拥有 LogSeverity 类型的variables,否则就可以了。
    • 这对我有用,只是做了一些小的改动。 @objcMembers public class LogSeverityBridge: NSObject { static func debug() -> String { return TravelerProtectionLevel.premium.rawValue }
    【解决方案5】:

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

    例如:

    .h 文件

    typedef NSString *const ProgrammingLanguage NS_TYPED_ENUM;
    
    FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageSwift;
    FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageObjectiveC;
    

    .m 文件

    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 的“与 C API 交互”中阅读有关此技术的更多信息

    【讨论】:

    • 这正是我一直在寻找的方法!
    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

      这是我的用例:

      • 我尽可能避免使用硬编码字符串,以便在更改某些内容时收到编译警告
      • 我有一个来自后端的固定字符串值列表,也可以是 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
              }
          }
      }
      

      【讨论】:

      • 我不能使用来自 Objective-C 的 [InventoryItemType fromString:]:它给出错误“接收器类型 'InventoryItemType' 不是 Objective-C 类”
      • 那是因为在 Objective-C 中 InventoryItemType 将是 Int 或 NSInteger 类型,它不是一个类。
      • 嗨@ChrisGarrett 我得到了这样的枚举:public enum TrackingValue { case constant(String) case customVariable(name: String) case defaultVariable(DefaultVariable) public enum DefaultVariable { case advertisingId case advertisingTrackingEnabled case appVersion case connectionType case interfaceOrientation case isFirstEventAfterAppUpdate case requestQueueSize case adClearId } } 让它被视为ObjC枚举的最佳方法是什么?提前致谢!
      • @agirault 您可以将其包装在另一个类中,例如class InventoryItemTypeParser: NSObject { @objc static func fromString(_ string: String?) -> InventoryItemType { return InventoryItemType.fromString(string) } }
      【解决方案8】:

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

      My error's 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
              }
          }
      }
      

      【讨论】:

      • 有什么方法可以从 Objc 创建这个枚举,当我尝试 [LogSeverity rawValue:] 它没有找到初始化程序。
      • 如何从目标 c 访问字符串值?
      【解决方案9】:

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

      【讨论】:

        【解决方案10】:

        您可以创建一个私有的Inner 枚举。该实现有点可重复,但清晰易懂。 1 行 rawValue,2 行 init,它们看起来总是一样的。 Inner 有一个方法返回“外部”等价物,反之亦然。

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

        如果您知道如何解决模板的可重复性问题,欢迎您在此答案的基础上再接再厉,我现在没有时间讨论它。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-02
          • 1970-01-01
          • 1970-01-01
          • 2015-10-14
          相关资源
          最近更新 更多