Group Group Group Group Group Group Group Group Group

Programming in Swift · Enumerations | Ray Wenderlich


#1

This is a companion discussion topic for the original entry at https://www.raywenderlich.com/5994-programming-in-swift/lessons/17

#2

Hi! Thank you for a great video. Can you tell me please how can I get value from twoDimensionalPoint without using switch statement? Is it possible?


#3

No, there’s really only one way to get the coordinates back, without a switch statement, and that’s with an if statement, checking for one specific case. That looks like this:

if case let .noZeroCoordinate(x, y) = twoDimensionalPoint {
  print(x)
  print(y)
}

However, later in this course, you’ll learn about Swift features which can allow you to write the switch statements only one time.

First, you’d move one switch statement into an initializer for TwoDimensionalPoint:

init(_ coordinates: (Double, Double) ) {
  switch coordinates {
  case (0, 0):
    self = .origin
  case (_, 0):
    self = .onXAxis(coordinates.0)
  case (0, _):
    self = .onYAxis(coordinates.1)
  default:
    self = .noZeroCoordinate(coordinates.0, coordinates.1)
  }
}

With that, you can make multiple TwoDimensionalPoints easily.

let x0y0 = TwoDimensionalPoint( (0, 0) )
let x1y0 = TwoDimensionalPoint( (1, 0) )
let x_2y3 = TwoDimensionalPoint( (-2, 3) )

Then, the other switch statement could move to a computed property, like this:

var coordinates: (Double, Double) {
  switch self {
  case .origin:
    return (0, 0)
  case let .onXAxis(x):
    return (x, 0)
  case .onYAxis(let y):
    return (0, y)
  case let .noZeroCoordinate(x, y):
    return (x, y)
  }
}

And finally, you could easily get the values you’re asking about!

x0y0.coordinates
x1y0.coordinates
x_2y3.coordinates

#4

Thank you! It helped me a lot.


#5

When I copied and pasted your code, I got the error: error: MyPlayground.playground:17:1: error: variables currently must have an initial value when entered at the top level of the REPL

let semester: Semester

Please help.


#6

Hello,
I’m using Xcode 10.1 on High Sierra 10.13.6.

Strangely, I find that when I type case something: typing the column doesn’t bring back the indentation to the beginning of the line. Is this a bug in playgrounds, something I am missing, something changed in the latest versions?

Thank you


#7

@shuler Please switch your playground from manual execution to automatic one to fix the issue and make the error go away. Thank you!


#8

@notationmaster This works fine for me, do you still have this issue?


#9

Unfortunately yes, I do…
For each case I used to type : and indentation would go back, now I have to do everything manually.

Settings in preferences seem to be good and even changing behaviour for the : doesn’t help.

Maybe a bug but just for me?


#10

Thanks for the video.

1- Can you please clarify what associated values are?
"Another way to associate values with your enumeration is to use “associated values” - but it’s not defined anywhere in the following examples, and their use is not explained.

2- I was trying out different months for the raw values operation (Month.december.rawValue - month.rawValue) and I found that if it isn’t within the same year, it doesn’t work. For example, if I set the ‘month’ constant to .october and want to know the number of months between October and the following month of June, is there any way to do that? (an operation for “number of months between these two months”)

Thank you.


#11
  1. All of the Doubles in the example are associated values.
enum TwoDimensionalPoint {
  case origin
  case onXAxis(Double)
  case onYAxis(Double)
  case noZeroCoordinate(Double, Double)
}
  1. The Month type is too simple to handle dates that differ from each other by more than a year, but here’s a way to handle what you said:

First, for ease of use, have Month conform to CaseIterable (which we cover in the later video on methods).

enum Month: Int, CaseIterable {

Then, you can use the count of all the months, like so:

let monthsBetween =
  (Month.june.rawValue + Month.allCases.count - month.rawValue)
  % Month.allCases.count

…which represents:

  1. Find the different between next June and month.
  2. If that’s more than a year, remove a year.

June is a special case. This code will return zero for June, but you might want 12 instead. You could put in the extra effort to make that happen, but I’d recommend continuing the course and learning about Structures instead. Then you’ll get an idea of how to start using Apple’s Date and Calendar types which are really what you’ll want for performing real-world date arithmetic.


#12

Thanks for the detailed reply! I’ll definitely come back and review the 2nd part again once I’ve gone over CaseIterable in Methods.

Just to make sure I got this right, do all enums have associated values then? Technically, you always have different cases that are associated with different values. The closest I got to a definition is in an article on here (“Getting to Know Enum, Struct and Class Types in Swift”) where it says that “…each enum case can be paired with its own data” and that the data are the associated values. Are those the different “representations” of the enum?

One last thing, in the coordinates example, you used mostly doubles (in parentheses after the case is named). What other type of data can be used? I tried Bool (I was trying to use ranges of temperature) but that didn’t work at all. In the article mentioned above, the author uses other enums (I think?).


#13

do all enums have associated values then?

No.

First, it’s possible and common to have enumerations without raw or associated values. Semester is an example in this video.

Second, per the documentation:

Raw values are not the same as associated values. Raw values are set to prepopulated values when you first define the enumeration in your code[…]. The raw value for a particular enumeration case is always the same. Associated values are set when you create a new constant or variable based on one of the enumeration’s cases, and can be different each time you do so.

There’s some trouble here with the word “associated”. The raw value is still literally associated with an enumation instance, but it’s not technically correct to use the term “associated value” for it, because that exact wording has special meaning in Swift.


What other type of data can be used?

There is one restriction I can think of but it’s advanced enough that we don’t even go into it in this course (Protocols with Associated Types). Everything else I know of will work.

I tried Bool (I was trying to use ranges of temperature) but that didn’t work at all.

How not? Feel free to post the example!

Edit: …I had a thought. Maybe you were trying to use Bool as a raw, not associated value type?

enum TemperatureRange: Bool {

That won’t work. The requirement for raw values is that they conform to the RawRepresentable protocol.


#14

The difference between case values, raw values, and associated values is much clearer now, thanks so much! I updated my notes :slight_smile:.

Some of the confusion came from using an Int type when talking about raw values in the video. I thought the raw values were like a ranking system/index (0 to the count of cases). They also increment by 1. (I’m guessing there’s an extra step for Floats and Doubles because those gave me error messages - I tried to see if I start at 2.2 for example, if the following case’s raw value would be 3.2). Strings can also be used apparently, but they have to be unique for each case…

It would be really helpful to have some non-numerical examples in future lessons/revisions if possible. I found the ghost on different floors examples easy to follow along with for example (and the narrative was enjoyable too!).

Edit: …I had a thought. Maybe you were trying to use Bool as a raw, not associated value type?

…Guily as charged! I tried it there, and I tried it after each case (in the video example, Doubles were used. I’m not sure what I was trying to accomplish).

I had messed up on the ranges and was trying to use conditions when really there was a much simpler solution.

Here is the little code snippet I came up with:

enum WeatherFeels {
case freezing
case cold
case cool
case warm
case hot
case sweltering
}

let temperature = -8
let weather: WeatherFeels

switch temperature {
case -50 ..< -20:
    weather = .freezing
case -20 ..< -5:
    weather = .cold
case -5 ..< 10:
    weather = .cool
case 10 ..< 22:
    weather = .warm
case 22 ..< 30:
    weather = .hot
case 30 ... 60:
    weather = .sweltering
default:
    "You're on a different planet, pal"
}

Thanks again for all your help!