在 Swift 中创建一个枚举,可导出到 ObjC,基于另一个枚举:String



我在 Swift 中有一个enum Foo: String(因此不能导出到 Objective-C),我正在尝试在 Swift 中创建另一个枚举FooObjc来"包装"现有的枚举,以便它 1) 可用于 Objective-C 和 2) 可来回转换 (Foo<=>FooObjc)。原始的Foo枚举是我不想修改的框架的一部分。当然,如果我改用一个类,很容易做我想做的事,就像这样:

@objc public class FooObjc: NSObject {
public private(set) var foo: Foo
@objc public var string: String {
return foo.rawValue
}
@objc public init?(string: NSString) {
guard let foo = Foo(rawValue: string as String) else {
return nil
}
self.foo = foo
}
internal init(foo: Foo) {
self.foo = foo
}
}

(PS:无法从NSString继承,因为 Swift 编译器仍然不接受为该类创建初始值设定项)

好的,但我绝对不喜欢这种方法,因为那样我的Objective-C代码将是字符串类型的。我真的很想改用enum,因为毕竟,这就是它。这是我能得到的最不糟糕的工作版本:

@objc public enum FooObjc: Int, RawRepresentable {
case undefined = -1
case bar
case baz
// omitting many more cases
internal init?(_ foo: Foo?) {
if let foo = foo {
self = fooMapping.filter { $1 == foo }.map { $0.key }.first!
} else {
self = .undefined
}
}
// MARK: - RawRepresentable
public typealias RawValue = String
public init?(rawValue: RawValue) {
let filter = fooMapping.filter { $1?.rawValue == rawValue }
guard filter.count > 0 else {
return nil
}
self = filter.map { $0.key }.first!
}
public var rawValue: RawValue {
switch (self) {
case .undefined: return "undefined"
case .bar: return Foo.bar.rawValue
case .baz: return Foo.baz.rawValue
// omitting many more cases
}
}
}
private let fooMapping: [FooObjc: Foo?] = [
.undefined : nil,
.bar : .bar,
.baz : .baz
// omitting many more cases
]

请注意:

  1. fooMapping对于避免每个初始值设定项出现一个开关大小写很有用
  2. 这种undefined情况是必要的,因为在 Swift 中你可以有可选的enum属性,而在 Objective-C 中你不能,所以这种情况将直接映射到一个值nilFoo?

这里让我担心的是,我不得不从原始 Foo 中写遍相同的案例......如果我只重复两次,我会完全满意,但我无法使用rawValue属性中的fooMapping,因为这样我就会在这两者之间得到一个循环。

注意:我不确定这是否与问题相关,但在原始enum中,某些情况具有特殊的字符串属性,例如我们只是case bar,但我们也有case baz = "something"

所以,问题是:是否有人建议改进这种方法,甚至提出一些全新的东西来避免如此多的代码重复?

谢谢!

这里让我

担心的是,我不得不从原始 Foo 中写三遍相同的案例

考虑一个数组["bar", "baz" ...]。通过查看此数组中字符串的索引并进行必要的调整,您可以从字符串转换为整数(因此,通过原始值转换为大小写)。通过索引到数组中进行必要的调整,您可以从整数转换为字符串(因此,通过原始值转换为大小写)。因此,您只需写出一次字符串值。这消除了您的两次重复。

你仍然需要在枚举声明中写出案例名称,因为没有其他方法可以告诉Objective-C案例的名称。显然,消除这种重复的方法是愿意改变Foo本身的实现,使其与Objective-C兼容。但你已经事先声明你拒绝这样做,所以现在你必须付出代价。

最新更新