Enum initialized with a non-existent rawValue does not fail and return nil
I filed a bug with Apple and this is the reply I received:
"Engineering has determined that this issue behaves as intended based on the following information:
Because C enums may have values added in future releases, or even have "private case" values used by the framework that are not included in the headers, there's no way to check whether a value provided in Swift is actually valid or invalid. Therefore,
init(rawValue:)
is obliged to produce a value just as a C cast would. There are discussions in the Swift Open Source project on how to improve this situation in later versions of Swift, but the initializer for MKMapType still won't return nil."
Thanks to Apple Engineering for this explanation.
Enum defined with NS_ENUM's rawValue initializer doesn't fail
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 to
nil
. 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)!
Can you initialize an Enum value from the name of its case (*not* its RawValue?)
In Swift 4.2, this is quite easy to do now using CaseIterable
.
enum MyOtherEnum: CaseIterable {
case xxx
case yyy
case zzz
init?(caseName: String) {
for value in MyOtherEnum.allCases where "\(value)" == caseName {
self = value
return
}
return nil
}
}
enum MyTestEnum: Int, CaseIterable{
case one = 1
case eight = 8
case unknown = -1
init?(caseName: String) {
for value in MyTestEnum.allCases where "\(value)" == caseName {
self = value
return
}
return nil
}
}
What I am doing here is creating a failable initializer which iterates through all potential cases, testing to see if "\(value)"
(which returns the name for that potential case) matches the caseName
argument passed in to the initializer.
When a match is found, self
is set and the loop ends. Otherwise, nil
is returned for the call.
Below, are two working and two failing examples:
let myOtherEnum = MyOtherEnum(caseName:"xxx")
print(myOtherEnum) // MyOtherEnum.xxx
let myTestEnum = MyTestEnum(caseName:"eight")
print(myTestEnum?.rawValue) // 8
let myOtherEnumFail = MyOtherEnum(caseName:"aaa")
print(myOtherEnumFail) // nil
let myTestEnumFail = MyTestEnum(caseName:"ten")
print(myTestEnumFail) // nil
Check if a value is present in enum or not
You can simply try to initialize a new enumeration case from your string or check if all cases contains a rawValue
equal to your string:
let string = "categories"
if let enumCase = HomeDataType(rawValue: string) {
print(enumCase)
}
if HomeDataType.allCases.contains(where: { $0.rawValue == string }) {
print(true)
}
Codable enum with default case in Swift 4
You can extend your Codable
Type and assign a default value in case of failure:
enum Type: String {
case text,
image,
document,
profile,
sign,
inputDate = "input_date",
inputText = "input_text" ,
inputNumber = "input_number",
inputOption = "input_option",
unknown
}
extension Type: Codable {
public init(from decoder: Decoder) throws {
self = try Type(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
}
}
edit/update:
Xcode 11.2 • Swift 5.1 or later
Create a protocol that defaults to last case of a CaseIterable & Decodable
enumeration:
protocol CaseIterableDefaultsLast: Decodable & CaseIterable & RawRepresentable
where RawValue: Decodable, AllCases: BidirectionalCollection { }
extension CaseIterableDefaultsLast {
init(from decoder: Decoder) throws {
self = try Self(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? Self.allCases.last!
}
}
Playground testing:
enum Type: String, CaseIterableDefaultsLast {
case text, image, document, profile, sign, inputDate = "input_date", inputText = "input_text" , inputNumber = "input_number", inputOption = "input_option", unknown
}
let types = try! JSONDecoder().decode([Type].self , from: Data(#"["text","image","sound"]"#.utf8)) // [text, image, unknown]
Initialize enum from associated value
Yes you can
enum class DirectionSwiped(val raw: Int){
LEFT(4),
RIGHT(8);
}
val left = DirectionSwiped.LEFT
val right = DirectionSwiped.RIGHT
val leftRaw = DirectionSwiped.LEFT.raw
val rightRaw = DirectionSwiped.LEFT.raw
val fromRaw = DirectionSwiped.values().firstOrNull { it.raw == 5 }
This would be the correct way to access the instances of the enum class
What you are trying to do is create a new instance outside the definition site, which is not possible for enum
or sealed
classes, that's why the error says the constructor is private
Overriding Enum init?(rawValue: String) to not be optional
The default initializer is failable
. It means that if the received parameter does not match a valid enum case it does return nil
.
Now you want to do 2 incompatibles things:
- You want to redefine the default initializer making it not failable. In fact you want a default enum value created when the received param is not valid.
- Inside your redefined initializer you want to call a failable initializer (which no longer exists) using the same name of the new one.
This is not possible, I the 3 possible solutions as follows:
- Creating a different init
You define a new not failable initializer with a default value, a different parameter name and inside it you call the default failable initializer.
enum Language: String {
case english = "English", italian = "Italian", french = "French"
init(fromRawValue: String) {
self = Language(rawValue: fromRawValue) ?? .english
}
}
- Redefining the default init
You redefine the default initializer, you make it not failable and you write the full logic inside it.
enum Language: String {
case english = "English", italian = "Italian", french = "French"
init(rawValue: String) {
switch rawValue {
case "Italian": self = .italian
case "French": self = .french
default: self = .english
}
}
}
- Creating a static func
enum Language: String {
case english = "English", italian = "Italian", french = "French"
static func build(rawValue: String) -> Language {
return Language(rawValue: rawValue) ?? .english
}
}
Now you can build a Language
value writing:
let italian = Language.build(rawValue: "Italian") // Italian
let defaultValue = Language.build(rawValue: "Wrong input") // English
How to get enum from raw value in Swift?
Too complicated, just assign the raw values directly to the cases
enum TestEnum: String {
case Name = "Name"
case Gender = "Gender"
case Birth = "Birth Day"
}
let name = TestEnum(rawValue: "Name")! //Name
let gender = TestEnum(rawValue: "Gender")! //Gender
let birth = TestEnum(rawValue: "Birth Day")! //Birth
If the case name matches the raw value you can even omit it
enum TestEnum: String {
case Name, Gender, Birth = "Birth Day"
}
In Swift 3+ all enum cases are lowercased
How can a return a blank value for my Enum
Why not use nil
?
First declare your property as optional:
var type: SupportType?
And then you can pass nil:
NewType(name: data.name, supportType: nil, supportName: "")
Enum class in swift
Why don't you simply add a static property to your enum Type?
enum Type: Int {
case A = 0, B, C
static let all = [A, B, C]
}
Related Topics
Programmatically Place Partial Image Over Another in UIview Using Swift 3
Xcode Error: Ar Reference Image Must Have Non Zero Positive Width
How to Initialize UIbezierpath to Draw a Circle in Swift
Can't Load Images on MAC Screensaver Release Build (It Works on Xcode Debug Build)
How to Detect If The User Was Deleted from Firebase Auth
Advantage of Key-Value Coding in Swift 4
Position of Mouse Click Relative to Scene, Not Window
Uipangesturerecognizer Is Not Working in iOS 13
Xcode Server: Opening Import File for Module 'Mobilecoreservices': Permission Denied
Custom Radix Columns (+Special Characters)
iOS 12 Wkwebview Not Working with Redirects
Close UIdatepicker After Selection When Style Is .Compact
Easiest Way to Truncate Float to 2 Decimal Places
Navigation Link in Bar Items Goes Back to Top of Navigationview
Why Is My Libraries Not Able to Expand on The Cocoapods and Shows as Objective-C Not Swift