Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best way to declare a list of scalar values in Swift

Tags:

swift

I was wondering: What is the best way to declare a non-ordered list of scalar values in Swift, that are fully backed by a symbol and are liable to be checked by the compiler?

Let's say I want to declare a list of LetterSpacing values that I want to access and use in my code via their symbols.

My understanding is that I should declare an swift enum like this:

enum LetterSpacing: Double {
    case Short = 1.0
    case Medium = 2.0
    case Large = 3.0
}

This works fine and gets compiler checks but the annoying part is that I need to LetterSpacing.XXX.rawValue each time to access the underlying value. The .rawValue bothers me a bit to have to specify this everywhere.

I suppose it wouldn't make sense or would be inappropriate to declare a struct with three static let Short = 1.0 etc..

So, how would you handle such cases? Is it possible to add somekind of protocol/extension swift magic to the existing types to be able to have the Short enum act as its own value? (i.e. let size = LetterSpacing.Short would be of type Double and have a value of 1.0)

like image 646
Nicolas B. Avatar asked Jun 26 '15 12:06

Nicolas B.


2 Answers

The enum would be preferred for multiple reasons.


First, the enum actually ends up with a smaller memory footprint. This is because Swift optimizing enum values into a single byte (no matter what the raw value is), and we only get the full value out of it when we call rawValue on the enum value. Paste the following code into a playground to see:

struct LetterSpacingS {
    static let Short: Double = 1.0
}

enum LetterSpacingE: Double {
    case Short = 1.0
}

sizeofValue(LetterSpacingS.Short)
sizeofValue(LetterSpacingE.Short)

The double is 8 bytes, while the enum is just 1.


Second, the importance of the first point extends beyond just memory footprint. It can apply to the execution efficiency of our program as well. If we want to compare two values from our struct, we're comparing 8 bytes. If we want to compare two of our enum values, we comparing just a single byte. We have 1/8th as many bytes to compare in this specific case. And this is just with a double. Consider that Swift enums can also have a string as a backing type. Comparing two strings can be extraordinarily expensive versus just comparing the single byte they're hidden behind in an enum.


Third, using the enum, we're not comparing floating point numbers, which you really don't want to do, generally speaking.


Fourth, using the enum gives us some type safety that we can't get with the struct of constants. All the struct of constants does is let us define a handful of predefined constants to use.

struct LetterSpacingS {
    static let Short = 1.0
    static let Medium = 2.0
    static let Long = 3.0
}

But now what would a method look like that expects one of these values?

func setLetterSpacing(spacing: Double) {
     // do stuff
}

And now what happens when Joe-Coder comes in and does:

foo.setLetterSpacing(-27.861)

Nothing's stopping him. It takes a double, any double. This might not be so common, but if this is in a library you're distributing, you are leaving yourself open to questions and comments like "When I pass a value of 5.0 in, it doesn't look right at all!"

But compare this to using the enum.

func setLetterSpacing(spacing: LetterSpacing) {
    // do stuff
}

And now, we only get the three predefined choices for what to pass into this argument.

And outside of your internal use for the values held by the enum, you should have to use rawValue too much.


The fifth reason isn't a strong reason for using an enum over a struct, but mostly just a point to make to eliminate one of the reasons suggested for using a struct over an enum. Swift enums can be nested in other types, and other types can be nested in Swift enums. You don't have to use a struct to get nested types.

like image 165
nhgrif Avatar answered Nov 14 '22 21:11

nhgrif


I don't see why creating a Struct with three static constants would be inappropriate. I see a lot of Swift code (also from Apple) that does this.

Structs lets you create nice hierarchies:

struct Style {
    struct Colors {
      static let backgroundColor = UIColor.blackColor()
      ...
    }

    struct LetterSpacing {
      static let Short = 1.0
    } 

    ...
}
like image 26
diederikh Avatar answered Nov 14 '22 19:11

diederikh