仅针对 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 varfunc,但也有可能不会只是为了向 Obj-C 消费者隐藏它,而且还使用 @objc(AnotherName) 模式以不同的名称向他们公开。

对于我的特定用例,这允许我以库前缀名称将 enum 公开给 Obj-C,与使用未受保护的基于字符串的 API 相比,这绝对是一个更好的选择。

【讨论】:

以上是关于仅针对 Objective-C 导出 Swift var的主要内容,如果未能解决你的问题,请参考以下文章

针对 Objective-C 枚举的新 Swift 5 警告:如何摆脱它们?

从Objective-C向Swift转换学习到的经验

如何从 Swift 单元测试访问测试目标中存在的 Objective-C 代码?

在 Swift 和 Objective-C 项目中使用 CocoaPods

Swift Bridging-Header Objective-C presentViewController

Swift Xcode 自动布局约束仅针对 iphone 6 plus 和 ipad