Reputation: 6270
I have an Objective-C file with an enum defined like:
typedef NS_ENUM(NSInteger, State) {
State_ACTIVE = 0,
State_PENDING = 1,
State_CANCELED = 2
};
In my swift code, if I do let state = State(rawValue: 100)
, usually this should return nil
, since it's a failable initializer. However, when the enum is declared as such (with NS_ENUM
), initialization succeeds, and there's no indication that that is an invalid enum value. Is this a bug in Xcode, or working as intended?
Upvotes: 3
Views: 4317
Reputation: 41
It is possible to define an extra initialiser in an extension
of the enumeration to implement the (Swift-like) behaviour to suit your needs:
extension State
{
static var allRawCases: [Int]
{
var array = [Int]()
switch State.ACTIVE
{
case .ACTIVE:
array.append(State.ACTIVE.rawValue)
fallthrough
case .CANCELED:
array.append(State.CANCELED.rawValue)
fallthrough
case .PENDING:
array.append(State.PENDING.rawValue)
}
return array
}
}
extension State
{
init?(raw: Int)
{
guard State.allRawCases.contains(raw) else
{
return nil
}
self = State(rawValue: raw)!
}
}
Pros:
If you add / remove enum case in Objective-c code the compiler will display an error and you will adopt the code.
New constructor behaves as default constructor of a native swift enumeration (without associated values).
In next piece of code it's better to have a nil
variable instead of an "invalid" value. This value leads to a crash in swift 3 + Xcode 10
project.
// intValue differs from raw enumeration values e.g 100
guard let state = State(rawValue: intValue) else
{
return
}
switch state
{
case .ACTIVE:
// do active staff
case .CANCELED:
// do canceled staff
case .PENDING:
// do pending staff
}
Cons:
allRawCases
property grows proportionally to the number of elements in the Objective-c enumeration.Upvotes: 0
Reputation: 42449
This is intended behavior. For any NS_ENUM
s bridged to Swift the constructor will never return nil
.
Try it with some other enums in the iOS SDK bridged to Swift with unexpected values. They will all return non-nil, even for a rawValue
that is not defined by the enum:
UITableViewCellStyle(rawValue: 7) // "Optional(__C.UITableViewCellStyle)"
UITableViewCellAccessoryType(rawValue: 9999) // "Optional(__C.UITableViewCellAccessoryType)"
or, with unsafeBitCast
:
unsafeBitCast(42, UITableViewCellEditingStyle.self) // "Optional(__C.UITableViewCellStyle)"
Martin R pointed out that this is documented in the Xcode 6.3 release notes:
Imported NS_ENUM types with undocumented values, such as
UIViewAnimationCurve
, can now be converted from their raw integer values using theinit(rawValue:)
initializer without being reset tonil
. Code that usedunsafeBitCast
as a workaround for this issue can be written to use the raw value initializer. For example:let animationCurve = unsafeBitCast(userInfo[UIKeyboardAnimationCurveUserInfoKey].integerValue, UIViewAnimationCurve.self)
can now be written instead as:
let animationCurve = UIViewAnimationCurve(rawValue: userInfo[UIKeyboardAnimationCurveUserInfoKey].integerValue)!
Upvotes: 1