Invalid redeclaration of rawValue in Release build

1.1k Views Asked by At

I have a mixed project and came across an interesting issue. There's an enum, defined in obj-c

typedef NS_ENUM (NSUInteger, ABCCategory) {
  ABCCategoryFirst,
  ABCCategorySecond
};

Next, there's a swift file where an extension is defined

extension ABCCategory: RawRepresentable {

  public typealias RawValue = String

  public init(rawValue: RawValue) {
    switch rawValue {
    case "first":
      self = .first
    case "second":
      self = .second
    default:
      self = .first
    }
  }

  public var rawValue: RawValue {
    get {
      switch self {
      case .first:
        return "first"
      case .second:
        return "second"
      }
    }
  }
}

Everything works fine in the Debug configuration, but when I switch to Release it does not build, saying: Invalid redeclaration of 'rawValue' I've tried removing typealias, replacing RawValue with String (so the protocol could implicitly guess the value), making constructor optional as in the protocol (and implicitly unwrapped optional also) - no go.

I do understand that extending an Int enum with string is a bit weird, but why it stops building in Release and working absolutely perfect in Debug?

Is there some different mechanism of treating enums/classes/extensions for Release configuration?

1

There are 1 best solutions below

0
On BEST ANSWER

The raw value syntax for enums in Swift is “just” a shorthand for conformance to the RawRepresentable protocol. It’s easy to add this manually if you want to use otherwise unsupported types as raw values. Source

I'm not sure why it works in debug because when you create a typed enum you are already 'conforming' to RawRepresentable. So when you create an NS_ENUM it is imported in to swift like so:

public enum ABCCategory : UInt {
    case first
    case second
}

Meaning that it already conforms to RawRepresentable. The fix can be achieved two ways, one in Swift and in Objective-C


In Swift we just remove the RawRepresentable and change rawValue to stringValue, and RawValue to String:

extension ABCCategory {

    var stringValue: String {
        switch self {
        case .first: return "first"
        case .second: return "second"
        }
    }

    init(_ value: String) {
        switch value {
        case "first":
            self = .first
        case "second":
            self = .second
        default:
            self = .first
        }
    }

}

Or you could just change the Objective-C to use NS_TYPED_ENUM. Some info here. However this will change your enum to a struct

.h

typedef NSString *ABCCategory NS_TYPED_ENUM;

extern ABCCategory const ABCCategoryFirst;
extern ABCCategory const ABCCategorySecond;

.m

ABCCategory const ABCCategoryFirst = @"first";
ABCCategory const ABCCategorySecond = @"second";

This will be imported by swift like so:

public struct ABCCategory : Hashable, Equatable, RawRepresentable {

     public init(rawValue: String)
}

public static let first: ABCCategory
public static let second: ABCCategory