仅针对 Objective-C 导出 Swift var
Posted
技术标签:
【中文标题】仅针对 Objective-C 导出 Swift var【英文标题】:export Swift var for Objective-C only 【发布时间】:2021-04-07 11:26:43 【问题描述】:我的 Swift 库 API 有一个 enum
类型的公共属性:
public enum Animal : String, Codable
case Dog
case Cat
public var pet: Animal
enum
未导出为@objc
,但我需要从 Objective-C 访问此属性。所以,我添加了一个计算属性
@objc public var petAsString: String
get return String(reflecting: pet)
set pet = newValue == "Cat" ? Animal.Cat : Animal.Dog
我可以隐藏这个属性对我的库的 Swift 使用者吗?或者,也许有更好的方法让它在 Objective-C 中使用,没有丑陋的“AsString”后缀?
更新: 对不起,我歪曲了我的根本问题。我选择经历所有这些麻烦,因为那时我不明白 Swift typealias
让我可以将一个长而丑陋但明确的 enum AlexPetLib_Animal
导出到 Objective-C,而我和第三方的所有 Swift 代码仍然可以使用正确命名空间的 @ 987654328@别名:
public typealias Animal = AlexPetLib_Animal
@objc public enum AlexPetLib_Animal : Int, Codable
case Dog
case Cat
我的库代码不需要额外的更改;只需重新编译使用该库的 Swift 应用程序。
【问题讨论】:
为什么要使用String(reflecting:)
而不是rawValue
?您不需要任何类型别名,您只需使用 @objc(AlexPetLib_Animal)
为 Objective-C 指定一个名称。
@Sulthan 谢谢,这个@objc(AlexPetLib_Animal)
是我所缺少的!至于String(reflecting:)
– 这只是一个插图,请忽略它。
【参考方案1】:
具有整数原始值类型的枚举可以是@objc
,因此您可以只使用Int
作为Animal
的原始值类型:
@objc
public enum Animal : Int, Codable
case dog
case cat
要在 Swift 中复制 String
原始值类型的行为,请添加以下成员:
var stringValue: String
switch self
case .cat: return "Cat"
case .dog: return "Dog"
init?(stringValue: String)
switch stringValue
case "Cat": self = .cat
case "Dog": self = .dog
default: return nil
public init(from decoder: Decoder) throws
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)
if let animal = Animal(stringValue: string)
self = animal
else
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Expected cat or dog!")
public func encode(to encoder: Encoder) throws
var container = encoder.singleValueContainer()
try container.encode(stringValue)
与“使用Animal
方法为每个事物编写两个版本”不同,这四个成员是您唯一需要的额外事物。之后,您将能够将使用Animal
的所有内容标记为@objc
(当然假设它不使用其他非@objc
的东西)。
一种极端情况是,当将Animal
传递给接受通用RawRepresentable
的方法时,它将使用Int
原始值,但我不会经常发生这种情况...
【讨论】:
不导出枚举的主要原因是为了避免命名空间冲突。我不想在我和第 3 方 swift 代码中的任何地方都将Animal
前缀为 AlexPetLibAnimal
,只是因为有人可以在 Objective C 中使用它。我认为 Swift 的 typealias
对我来说已经足够好了。
@AlexCohn 您可以将其导出为 ObjC 中的其他名称。在枚举上尝试@objc(AlexPetLibAnimal)
。
和@Sulthan 建议的一样,谢谢。它甚至比 typealias 更干净:它使用 SWIFT_ENUM_NAMED
这可能会有所帮助,但仍在学习这个 __attribute__((swift_name("Animal")))
does 是什么。
@AlexCohn 啊,我没有看到 Sulthan 的评论。我不确定您在谈论__attribute__((swift_name("Animal")))
。你不是将 swift 枚举暴露给 ObjC 吗?如果我理解正确,那应该完全无关紧要。
Swift 将其枚举(在AlexPetLib-Swift.h
文件中)导出为typedef SWIFT_ENUM_NAMED(NSInteger, AlexPetLibAnimal, "Animal", open)
的方式。这个宏注入了__attribute__
。你是对的,这个属性是为 objc2swift 集成而设计的,但是当我尝试通过 CocoaPod 使用我的 lib 作为 xcframework 时,swift 编译器正在查看这个属性!到目前为止,我发现了一个问题:类内的枚举获得了类外的别名,但这也发生在“正常”@objc
上。【参考方案2】:
总结一下:没有任何技巧可以向 Swift 消费者隐藏 Swift var 或 func,但也有可能不会只是为了向 Obj-C 消费者隐藏它,而且还使用 @objc(AnotherName)
模式以不同的名称向他们公开。
对于我的特定用例,这允许我以库前缀名称将 enum 公开给 Obj-C,与使用未受保护的基于字符串的 API 相比,这绝对是一个更好的选择。
【讨论】:
以上是关于仅针对 Objective-C 导出 Swift var的主要内容,如果未能解决你的问题,请参考以下文章
针对 Objective-C 枚举的新 Swift 5 警告:如何摆脱它们?
如何从 Swift 单元测试访问测试目标中存在的 Objective-C 代码?
在 Swift 和 Objective-C 项目中使用 CocoaPods