TinySolution: Resolve the restrictions of enum

Galvin Li
5 min readJan 13, 2019

--

此文章同时提供中文版本:TinySolution:解决enum的限制

Swift’s enum is much more powerful than Objective-C. We can create case values based on various types with different parameters, which are useful in situations where different states or objects have distinct data types. For example the Result type is very useful.

BTW: Result type would be official supported in Swift5. (SE-0235)

Although enum is powerful, there are some restrictions. For example, we often need to draw two lines, solid and dashed, with custom line width. At this time we can implement this with enum.

Things never be that simple in the real project

In addition to the line width, the dash line needs to support custom width of dash and space. Of cause we can change Line to the following format:

The problem is solved, but most of the dash lines use the same width of dash and space. We don’t want to manually pass the same value every time, so we try to use the default parameters:

Unfortunately, the compiler tells us that enum does not support the default parameters. But we had this limitation with Objective-C for long time and we are familiar with how to implement the default parameters in other way. So we changed to this:

The compiler tells us everything works. The creation of the three case objects is also successful. So the problem is solved? At least it seems to be the case.

Strange phenomenon

When you start writing the switch method, things just not work as your expect. For example, I added a method of output() and a warning appeared:

It seems like the previous case dash(...) contains all the cases of the latter one. Considering that, let’s exchange it:

However, the same warning is still appear. It seems those two case dash(...) are exactly same for the compiler, so we can’t find out the exact case.

If this is all the problem, this kind of implementation still useful in some situations. Unfortunately, it isn’t. Let’s write a few tests based on the last implement of Line:

You can see that Line.dash(width:1) is judged as default and does not belong to Line.dash. So this kind of implement is already be an exception. The latter case will overwrite the former one. The former one can only be created and not be judged by switch, so basically this case is useless.

Maybe this is a Swift bug? Please tell me if you know.

Solution

Since this is TinySolution, we certainly have to give a solution to the problem. As simple implementation we can manually create a static func dash to replace the case dash(width: Float) like this:

Then we can correctly output and judge our Line object:

However, mixing the automatic generate initialization methods and manual create initialization methods is always a bit impure, especially if some configuration needs to be performed during initialization, the flexibility of the those initialization method is inconsistent. Because if we try to override the automatically generated static method, there will be a redeclaration error:

Better solution

Of course, since we can define an initialization method, we can change the case name and create all the initialization methods ourselves. But in this case there are two sets of initialization methods mixed and have different behavior, which is not good. And this object is still enum, when we switch it we will see a set of cases that are completely inconsistent with the initialization.

I think this is not a good solution, so I recommend another clearer implementation like this:

First, we wrap the original enum Line with a struct Line. To prevent the name conflict we change the name of the enum Line to enum Content. So the struct object holds the enum object. Then we extract all method of enum to the struct layer and add static func to achieve enum creation effect. Because it is a static func, our default parameters can be use again. After all these changes we still won’t need to modify the creation code of the previous enum object, they just work.

  • All code in this article can be found in the GitHub project.
  • If you have questions or suggestions, welcome to leave comment for discuss.
  • If you feel this article is valuable, please forward it so more people can see it.
  • If you like this type of content, welcome to follow my Medium and Twitter, I will keep posting useful content for everyone.

--

--

Galvin Li

A Tiny iOS developer who love to solve problems and make things better.