How should I implement Default Associated Values with Swift Enums?
I know this is a bit old, but would this work for what you want?
typealias FilterIdentifier = String
enum DefaultAPIFilters: FilterIdentifier {
case Everyone = "everyone"
case Team = "team"
}
enum APIFilters {
case Default(DefaultAPIFilters)
case Custom(FilterIdentifier)
}
let everyoneFilter = APIFilters.Default(.Everyone)
let teamFilter = APIFilters.Default(.Team)
let clownFilter = APIFilters.Custom("clowns_only")
Enum with associated value of default type
In this situation it might be easier to define it like this:
enum DeviceType {
case phone
case watch
case tablet
}
struct Device {
var type: DeviceType
var name: String
... init, etc.
}
Then you can handle the type and string independently of each other, because if every single enum case has a string it sounds like maybe the string is not directly related to the enum value.
What is the best approach to provide default value for enum in Swift?
You can fix it by using the init(rawValue:)
initializer for RawRepresentable
enums. I also constrain the RawValue
to String
-only enums.
This does require you still mark the enum as having a String
raw value in the enum
itself.
Code:
protocol StringEnum: RawRepresentable, ExpressibleByStringLiteral where RawValue == String {
static var `default`: Self { get }
}
extension StringEnum {
init(stringLiteral value: String) {
guard let validValue = Self(rawValue: value) else {
self = Self.`default`
return
}
self = validValue
}
}
enum TrackType: String, StringEnum {
static let `default` = TrackType.unsupported
case video
case audio
case subtitles
case unsupported
}
Usage:
let value: TrackType = "foobar"
print(value)
With 'foobar
' the result is 'unsupported
'. With 'video
', the result is 'video
'. It's working correctly.
Default arguments in Swift enums
FYI: Default parameter values are now allowed after Swift 5.1. This answer only applies to versions of Swift before that.
This is a tricky one because you cannot have default values, you cannot have stored properties and you cannot repeat case names in enums. The best thing I can think to do is to create an init() and some semi-private cases.
enum SnapOperationQueuePriority {
case Highest, High, Low, Default
}
enum SnapOperationQueue {
case Highest, High, Normal, Low
case _Highest(lowerThreshold: SnapOperationQueuePriority)
case _High(lowerThreshold: SnapOperationQueuePriority, higherThreshold: SnapOperationQueuePriority)
case _Normal(lowerThreshold: SnapOperationQueuePriority, higherThreshold: SnapOperationQueuePriority)
case _Low(higherThreshold: SnapOperationQueuePriority)
init(queue:SnapOperationQueue, lowerThreshold:SnapOperationQueuePriority = .Default, higherThreshold:SnapOperationQueuePriority = .Default) {
switch queue {
case .Highest:
self = ._Highest(lowerThreshold: lowerThreshold == .Default ? .Highest : lowerThreshold)
case .High:
self = ._High(lowerThreshold: lowerThreshold == .Default ? .Low : lowerThreshold, higherThreshold: higherThreshold == .Default ? .High : higherThreshold)
case .Normal:
self = ._Normal(lowerThreshold: lowerThreshold == .Default ? .Low : lowerThreshold, higherThreshold: higherThreshold == .Default ? .High : higherThreshold)
case Low:
self = ._Low(higherThreshold: higherThreshold == .Default ? .Low : higherThreshold)
default:
self = queue
}
}
}
SnapOperationQueue.Normal
SnapOperationQueue(queue: .Normal)
SnapOperationQueue(queue: .High, lowerThreshold: .High, higherThreshold: .Highest)
This keeps old implementations valid and catches new ones made using init. In addition you might add a method like this to the enum:
func queue() -> SnapOperationQueue {
switch self {
case .Highest:
return SnapOperationQueue(queue: .Highest)
case .High:
return SnapOperationQueue(queue: .High)
case .Normal:
return SnapOperationQueue(queue: .Normal)
case Low:
return SnapOperationQueue(queue: .Low)
default:
return self
}
}
So that you can transform the old types of enum cases into the new, e.g. SnapOperationQueue.Normal.queue()
Swift Enum associated values conforming to single protocol
I've found the solution in case anyone will need this too.
enum ContentType {
case content1(Type1 & Refreshable)
case content2(Type2 & Refreshable)
case content3(someLabel: Type3 & Refreshable)
func refreshMe() {
let caseReflection = Mirror(reflecting: self).children.first!.value
(caseReflection as? Refreshable)?.refresh() //If associated type doesn't have label
let refreshable = Mirror(reflecting: caseReflection).children.first?.value as? Refreshable
refreshable?.refresh() //If associated type has label
}
}
How to make a Swift enum with associated values equatable
SE-0185 Synthesizing Equatable and Hashable conformance has been implemented in Swift 4.1, so that it suffices do declare conformance to the protocol (if all members are Equatable
):
enum ViewModel: Equatable {
case heading(String)
case options(id: String, title: String, enabled: Bool)
}
For earlier Swift versions, a convenient way is to use that tuples can be compared with ==
.
You many also want to enclose the compatibility code in a Swift version check, so that the automatic synthesis is used once the project is updated to Swift 4.1:
enum ViewModel: Equatable {
case heading(String)
case options(id: String, title: String, enabled: Bool)
#if swift(>=4.1)
#else
static func ==(lhs: ViewModel, rhs: ViewModel) -> Bool {
switch (lhs, rhs) {
case (let .heading(lhsString), let .heading(rhsString)):
return lhsString == rhsString
case (let .options(lhsId, lhsTitle, lhsEnabled), let .options(rhsId, rhsTitle, rhsEnabled)):
return (lhsId, lhsTitle, lhsEnabled) == (rhsId, rhsTitle, rhsEnabled)
default:
return false
}
}
#endif
}
Is there a way in Swift to get an associated value without using a switch statement?
You can use if case .<enum_case>(let value)
as in TylerP's example,
or if case let .<enum_case>(value)
:
enum Foo {
case anInt(Int)
case aFloat(Float)
}
let aFoo: Foo = .anInt(9)
// Example of `if case .<enum_case)(let value)` syntax:
if case .anInt(let aValue) = aFoo {
print("aFoo = anInt(\(aValue))")
// Example of `if case let .enum_case(value)` syntax:
} else if case let .aFloat(aValue) = aFoo {
print("aFoo = aFloat(\(aValue))")
}
Both work. I'm not sure why the language includes both variants.
If you only care about one enum type, then either if
syntax makes sense to me. If you are dealing with more than one possible enum value then the switch version seems cleaner.
Swift 2: Is there any way to use 'default' in the switch statement of enum with associated values?
While I agree with Paul's concern that it is odd to nest Location
precisely this way, the basic problem is solvable. Personally, I wouldn't solve it with a default
, I'd just simplify the code and use the tools Swift gives us (like CustomStringConvertible
; I also put labels on your data; it was too confusing with just two Location
elements that had completely different meanings):
indirect enum Location: CustomStringConvertible {
case Title(String?)
case Region(Location)
case Area(title: Location, parent: Location)
case City(title: Location, parent: Location)
case Settlement(title: Location, parent: Location)
case Street(title: Location, parent: Location)
case House(title: Location, parent: Location)
var description: String {
func format(locs: (Location, Location)) -> String {
return [locs.0, locs.1].map{$0.description}.filter{$0 != ""}.joinWithSeparator(", ")
}
switch self {
case .Title(let title): return title ?? ""
case .Region(let title): return "\(title)"
case .House(let data): return format(data)
case .Street(let data): return format(data)
case .Settlement(let data): return format(data)
case .City(let data): return format(data)
case .Area(let data): return format(data)
}
}
}
Notice how I unload the entire tuple into data
. You don't have to break the tuple apart in pattern matching. Enumerations never have multiple associated data. They always have exactly one: a tuple. (The same is true of functions. All functions take one value and return one value. That value just might happen to be a tuple.)
But if you really wanted to get rid of that repeated return format(data)
, then you can through Mirror
. (You can solve a rather shocking number of things through Mirror
. You should be very careful before you do. This case is just duplicated typing, not duplicated logic. A little duplicated typing is not something you should create a lot of complexity to remove.)
Here's how you'd do it:
var description: String {
switch self {
case .Title(let title): return title ?? ""
case .Region(let title): return "\(title)"
default:
let m = Mirror(reflecting: self)
guard let locs = (m.children.first?.value as? (Location, Location)) else {
preconditionFailure("Unexpected data in enum. Probably missing a case somewhere.")
}
return [locs.0, locs.1].map{$0.description}.filter{$0 != ""}.joinWithSeparator(", ")
}
}
The lesson here is that the first child of an enum is a tuple of all of its data.
But using Mirror
is much more fragile (notice I opened the possibility of crashing). And while an enum is possibly a great tool here, you still may want to rethink this data structure.
Related Topics
Closures Return Value (Previously Completionblock)
App Crashes from IPA File But Runs Fine from Xcode
How to Subclass a Class Which Doesn't Have Any Designated Initializers
How to Add Custom Init for String Extension
How to Get the Kvc-String from Swift 4 Keypath
Xctestcase Optional Instance Variable
How to Implement a Swift Protocol Across Structs with Conflicting Property Names
iOS Swift Error: 'T' Is Not Convertible to 'Mirrordisposition'
Using State Variables as Inputs to a Func in Swiftui
Select All Text in a Nstextfield Using Swift
Passing Arguments to #Selector Method in Swift
Using Xcode to Cross-Compile Swift to Linux
Gcd Pattern for Chaining Async Operations While Piping the Results
iOS 12 Errors: Appears to Be from a Different Nsmanagedobjectmodel Than This Context'S
Increment Integer in Nsuserdefaults
Passing and Storing Closures/Callbacks in Swift