When I have a situation where I already know enum case statement I want to get the associated value of, is there a cleaner way than using a switch statement to pluck out the associated value?
To have to come up with a switch statement, provide multiple cases, or a default case just to extract the associated value is gaudy.
enum CircularReasoning {
case justPi(pi: Double)
case pizzaPie(howMany: Int)
}
var piInTheSky : Double
let whatLogic = CircularReasoning(pi: 3.1415926)
β¬οΈ πΈπ’π―π΅ π΅π° π’π·π°πͺπ₯ β¬οΈ
switch whatLogic {
case .justPi(let pi):
piInTheSky = pi!
default:
break
}
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.
Here's an adaptation of #DuncanC's excellent upvoted and accepted answer, as applied to a fictitious version of my real-world use case.
It illustrates a possible way to use his answer to reduce the space required to extract associated values, especially if one has a lot of one-off cases...
Note: Not implying this is appropriate or professional swift styling; it's clearly idiosyncratic, yet compact. (I usually don't compress things into one liners like this, unless they get really repetitive/redundant & produce lot of pointless vertical bloat).
enum RealmKey { case realmOfRealms, anyOldRealm, someOtherRealm }
.
.
.
enum SymbolToken {
case realm (key: RealmKey?)
case space (key: SpaceKey?)
case area (key: AreaKey?)
case region (key: RegionKey?)
case preserve (key: PeserveKey?)
case openParen
case closeParen
case logicalAnd
case logicalOr
case logicalNot
var realmKey : RealmKey? { if case .realm (let key) = self { return key } else { return nil } }
var spaceKey : SpaceKey? { if case .space (let key) = self { return key } else { return nil } }
var areaKey : AreaKey? { if case .area (let key) = self { return key } else { return nil } }
var regionKey : RegionKey? { if case .region (let key) = self { return key } else { return nil } }
var preserveKey : PreserveKey? { if case .preserve (let key) = self { return key } else { return nil } }
}
let realm = SymbolToken.realm(.realmOfRealms)
let realmKey = realm.realmKey
This is what I've tried and can't figure out where the error is coming from. Is there something missing? Syntax error? I tried doing similar with if-else in the function and also getting errors.
var steps = 0
func incrementSteps() -> Int {
steps += 1
print(steps)
return steps
}
incrementSteps()
let goal = 10000
func progressUpdate() -> Int {
let updated = progressUpdate()/goal
switch updated {
case (0.0..<0.1):
print("Great start")
case (0.11..<0.5):
print("Almost halfway")
case (0.51..<0.9):
print("Almost complete")
default:
print("Beat goal")
}
}
progressUpdate()
You need to specify updated as Double. And cast it back to Int when returning(if you require Int for your requirement).
Note: Also, you need to modify calling the progressUpdate function within progressUpdate definition which causes a recursion. If you want to do so you might want to give condition to break the loop.
func progressUpdate() -> Int {
let updated = Double(steps/goal)
switch updated {
case (0.0..<0.1):
print("Great start")
case (0.11..<0.5):
print("Almost halfway")
case (0.51..<0.9):
print("Almost complete")
default:
print("Beat goal")
}
return Int(updated)
}
I'm trying to create different cases matching ranges in a switch statement to check user's age:
let minimumAge: Int
let maximumAge: Int
if let age = Calendar.current.dateComponents([.year], from: date, to: Date()).year {
switch age {
case ..<minimumAge:
return .young
case (maximumAge+1)... :
return .old
default:
return .valid
}
}
I would like to use something like >.. to exclude maximumAge from executing the case .old. Is that possible?
Use either ... (maximumAge, which is you have done) or use minimumAge...maximumAge as suggest by #vacawama in the above comment, there's nothing like >..
switch age {
case ..<minimumAge:
return .young
case minimumAge...maximumAge :
return .valid
default:
return .old
}
this is my code:
enum SymptomPeriod {
case Day
case Night
}
enum SymptomType {
case Breathing(SymptomPeriod)
case Breathlessness(SymptomPeriod)
case Opression(SymptomPeriod)
case Cough(SymptomPeriod)
case ActivityLimited()
case SecureTreatment()
}
struct Symptom {
let type: SymptomType
let date: NSDate
}
And i have an array of symptoms.
let symptomList: [Symptom] = ...
My need is to filter the list of symptoms with the SymptomPerion criteria, i trying to do like this:
let daySymtoms = symptomList.filter { (symptom) -> Bool in
return symptom.type = ???
}
My problem is in the filter function.
(My goal is to use a filter function and not a loop)
A few suggestions
Use your struct as namespace
Instead of repeating the word Symptom (e.g. SymptomPeriod, SymptomType) you should put your enums into you Symptom struct
Rename SymptomType as Kind
Once you moved SymptomType into Symptom you can drop the Symptom part of the name. However using Type as name will create a conflict so you should rename it Kind.
Add the period computed property to Kind
This will make the filtering much easier
Here's the code
struct Symptom {
enum Period {
case Day
case Night
}
enum Kind {
case Breathing(Period)
case Breathlessness(Period)
case Opression(Period)
case Cough(Period)
case ActivityLimited()
case SecureTreatment()
var period: Period? {
switch self {
case Breathing(let period): return period
case Breathlessness(let period): return period
case Opression(let period): return period
case Cough(let period): return period
default: return nil
}
}
}
let kind: Kind
let date: NSDate
}
The solution
Now the filtering has become very easy
let symptoms: [Symptom] = ...
let filtered = symptoms.filter { $0.kind.period == .Day }
This is how i am doing it:
let daySymtoms = symtoms.filter { (symptom) -> Bool in
switch symptom.type {
case .Breathing(.Day), .Breathlessness(.Day), .Opression(.Day), .Cough(.Day):
return true
default:
return false
}
}
Let me know if you have more simple way to do it.
Consider:
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
}
let leftEdge = Line.Horizontal(0.0)
let leftMaskRightEdge = Line.Horizontal(0.05)
How can I access, say, lefEdge's associated value, directly, without using a switch statement?
let noIdeaHowTo = leftEdge.associatedValue + 0.5
This doesn't even compile!
I had a look at these SO questions but none of the answers seem to address this issue.
The noIdeaHowTo non compiling line above should really be that one-liner, but because the associated value can be any type, I fail to even see how user code could write even a "generic" get or associatedValue method in le enum itself.
I ended up with this, but it is gross, and needs me to revisit the code each time I add/modify a case ...
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
var associatedValue: CGFloat {
get {
switch self {
case .Horizontal(let value): return value
case .Vertical(let value): return value
}
}
}
}
Any pointer anyone?
As others have pointed out, this is now kind of possible in Swift 2:
import CoreGraphics
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
}
let min = Line.Horizontal(0.0)
let mid = Line.Horizontal(0.5)
let max = Line.Horizontal(1.0)
func doToLine(line: Line) -> CGFloat? {
if case .Horizontal(let value) = line {
return value
}
return .None
}
doToLine(min) // prints 0
doToLine(mid) // prints 0.5
doToLine(max) // prints 1
You can use a guard statement to access the associated value, like this.
enum Line {
case Horizontal(Float)
case Vertical(Float)
}
let leftEdge = Line.Horizontal(0.0)
let leftMaskRightEdge = Line.Horizontal(0.05)
guard case .Horizontal(let leftEdgeValue) = leftEdge else { fatalError() }
print(leftEdgeValue)
I think you may be trying to use enum for something it was not intended for. The way to access the associated values is indeed through switch as you've done, the idea being that the switch always handles each possible member case of the enum.
Different members of the enum can have different associated values (e.g., you could have Diagonal(CGFloat, CGFloat) and Text(String) in your enum Line), so you must always confirm which case you're dealing with before you can access the associated value. For instance, consider:
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
case Diagonal(CGFloat, CGFloat)
case Text(String)
}
var myLine = someFunctionReturningEnumLine()
let value = myLine.associatedValue // <- type?
How could you presume to get the associated value from myLine when you might be dealing with CGFloat, String, or two CGFloats? This is why you need the switch to first discover which case you have.
In your particular case it sounds like you might be better off with a class or struct for Line, which might then store the CGFloat and also have an enum property for Vertical and Horizontal. Or you could model Vertical and Horizontal as separate classes, with Line being a protocol (for example).
Why this is not possible is already answered, so this is only an advice. Why don't you implement it like this. I mean enums and structs are both value types.
enum Orientation {
case Horizontal
case Vertical
}
struct Line {
let orientation : Orientation
let value : CGFloat
init(_ orientation: Orientation, _ value: CGFloat) {
self.orientation = orientation
self.value = value
}
}
let x = Line(.Horizontal, 20.0)
// if you want that syntax 'Line.Horizontal(0.0)' you could fake it like this
struct Line {
let orientation : Orientation
let value : CGFloat
private init(_ orientation: Orientation, _ value: CGFloat) {
self.orientation = orientation
self.value = value
}
static func Horizontal(value: CGFloat) -> Line { return Line(.Horizontal, value) }
static func Vertical(value: CGFloat) -> Line { return Line(.Vertical, value) }
}
let y = Line.Horizontal(20.0)
You can get the associated value without using a switch using the if case let syntax:
enum Messages {
case ping
case say(message: String)
}
let val = Messages.say(message: "Hello")
if case let .say(msg) = val {
print(msg)
}
The block inside the if case let will run if the enum value is .say, and will have the associated value in scope as the variable name you use in the if statement.
With Swift 2 it's possible to get the associated value (read only) using reflection.
To make that easier just add the code below to your project and extend your enum with the EVAssociated protocol.
public protocol EVAssociated {
}
public extension EVAssociated {
public var associated: (label:String, value: Any?) {
get {
let mirror = Mirror(reflecting: self)
if let associated = mirror.children.first {
return (associated.label!, associated.value)
}
print("WARNING: Enum option of \(self) does not have an associated value")
return ("\(self)", nil)
}
}
}
Then you can access the .asociated value with code like this:
class EVReflectionTests: XCTestCase {
func testEnumAssociatedValues() {
let parameters:[EVAssociated] = [usersParameters.number(19),
usersParameters.authors_only(false)]
let y = WordPressRequestConvertible.MeLikes("XX", Dictionary(associated: parameters))
// Now just extract the label and associated values from this enum
let label = y.associated.label
let (token, param) = y.associated.value as! (String, [String:Any]?)
XCTAssertEqual("MeLikes", label, "The label of the enum should be MeLikes")
XCTAssertEqual("XX", token, "The token associated value of the enum should be XX")
XCTAssertEqual(19, param?["number"] as? Int, "The number param associated value of the enum should be 19")
XCTAssertEqual(false, param?["authors_only"] as? Bool, "The authors_only param associated value of the enum should be false")
print("\(label) = {token = \(token), params = \(param)")
}
}
// See http://github.com/evermeer/EVWordPressAPI for a full functional usage of associated values
enum WordPressRequestConvertible: EVAssociated {
case Users(String, Dictionary<String, Any>?)
case Suggest(String, Dictionary<String, Any>?)
case Me(String, Dictionary<String, Any>?)
case MeLikes(String, Dictionary<String, Any>?)
case Shortcodes(String, Dictionary<String, Any>?)
}
public enum usersParameters: EVAssociated {
case context(String)
case http_envelope(Bool)
case pretty(Bool)
case meta(String)
case fields(String)
case callback(String)
case number(Int)
case offset(Int)
case order(String)
case order_by(String)
case authors_only(Bool)
case type(String)
}
The code above is from my project https://github.com/evermeer/EVReflection
https://github.com/evermeer/EVReflection