Home > Software engineering >  Make enum that has a raw String type codable with Objective-C
Make enum that has a raw String type codable with Objective-C

Time:01-28

I have an enum that is codable:

public enum MyEnum: String, Codable, Hashable, Sendable {
  case one = "ONE"
  case two = "TWO"

  public init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    let rawValue = try container.decode(RawValue.self)
    self = MyEnum(rawValue: rawValue) ?? .one
  }
}

However, I now need to make this compatible with objective-c. I know that I can't have a String raw value, and it must be Int. I still need it to be compatible like before, as it's being created from JSON which is a string and not an Int. How do I do this?

CodePudding user response:

Rather than modifying your pure Swift type to expose it to Obj-C and hence restricting the language features that you can use, I'd suggest creating a separate bridge type that you expose to Obj-C.

This approach makes it much easier to model your data using the full capabilities of Swift and also make it easier to remove your Obj-C compatible types once your codebase is fully migrated to Swift (or at least all parts of it that require access to this type).

@objc public enum MyEnumBridge: Int {
  case one
  case two
}

Now add an extension to your Obj-C compatible type to initialise it from your pure Swift type:

extension MyEnumBridge {
  init(wrapped: MyEnum) {
    switch wrapped {
    case .one:
      self = .one
    case .two:
      self = .two
    }
  }
}

You can call the init from a Swift extension of your Obj-C type, but still store/use the enum in Obj-C.

And for converting the Obj-C type back to Swift:

extension MyEnumBridge {
  var wrapped: MyEnum {
    switch self {
    case .one:
      self = .one
    case .two:
      self = .two
    }
  }
}

CodePudding user response:

Okay I think I came up with a solution (correct me if I'm wrong!)

public enum MyEnum: Int, Codable, Hashable, Sendable {
  case one
  case two

  enum InternalMyEnum: String, Codable {
    case one = "ONE"
    case two = "TWO"
  }

  public func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()

    switch self {
    case .one:
      try container.encode(InternalMyEnum.one.rawValue)
    case .two:
      try container.encode(InternalMyEnum.two.rawValue)
    }
  }

  public init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    let rawValue = try container.decode(String.self)
    let internalMyEnum = InternalMyEnum(rawValue: rawValue) ?? .one
    switch internalVariantType {
    case .one:
      self = .one
    case .one:
      self = .one
    }
  }
}
  • Related