Skip to content

Instantly share code, notes, and snippets.

@davbeck
Created May 27, 2016 18:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davbeck/fd6de442cfdee51db7c5362513024747 to your computer and use it in GitHub Desktop.
Save davbeck/fd6de442cfdee51db7c5362513024747 to your computer and use it in GitHub Desktop.
Swift options

Swift options

Recently, 2 Swift Evolution proposal results have really disappointed me. Together they move Swift away from the flexibility of using method parameters as option lists.

SE-0060 Enforcing order of defaulted parameters, which was accepted, removes the ability to provide optional parameters in any order you like. I had actually hoped to go the other direction and extend this ability to include required parameters. Now, as you add and remove parameters to a call site, you have to make sure to do so in the proper order. As the number of parameters a method takes grows, and as the relation between them gets weaker, it becomes more and more difficult to determine the correct positioning without referring to documentation.

SE-0084 Allow trailing commas in parameter lists and tuples was rejected. This proposal sought to extend the ability to include a comma at the end of a list of parameters just like you can in arrays and dictionaries. I for one use the following syntax often:

let stuff = [
	"a",
	"b",
	"c",
]

Besides being clean to look at, with symmetry between the first and last elements, it is also functional; you can comment out any element without changing any other line. This also has the benefit of reducing diffs in version control when adding elements to the end of an array or changing it's order.

All of those arguments were brought up in the review process, but sadly it seems that the Swift community, and the core team in particular, want to discourage methods with long parameter lists. I find that to be disappointing. It's fairly common to have methods that take a list of options, often times initializers.

In Objective-C, this has been handled either with bitwise operators on special enums, or with NSDictionary. Both are available in Swift, with OptionSetType replacing bitwise operators. But OptionSetType only provides a list of boolean options, without any associated values, and dictionaries are not specific enough with their types, even in swift. For instance, NSAttributedString has attributes where each attribute expects a specific type of value. I had hoped that optional parameters would replace both of these cases, but it's starting to look like that isn't going to happen.

But, I've started using another form of options in my apps, and it is working really well. I first saw it used in Socket.IO-Client-Swift. The gist of it is to create a enum of all your options, with associated values where needed, then use Set to pass them in to a method. Here's an example:

func == (lhs: Notice.Option, rhs: Notice.Option) -> Bool {
	return lhs.hashValue == rhs.hashValue
}

class Notice {
	enum Option: Hashable {
		case Text(String)
		case SubtitleText(String)
		case BackgroundColor(UIColor)
		case Image(UIImage)
		case TimeInterval(NSTimeInterval)
		case DismissOnTap
		
		var hashValue: Int {
			switch self {
			case .Text(_):
				return 1
			case .SubtitleText(_):
				return 2
			case .BackgroundColor(_):
				return 3
			case .Image(_):
				return 4
			case .TimeInterval(_):
				return 5
			case .DismissOnTap:
				return 6
			}
		}
	}
	
	init(options: Set<Option>) {
		// ...
	}
}


Notice(options: [
	.Text("Easy options"),
	.DismissOnTap,
])

This solution has the best of all worlds. It has strict type enforcement like parameters, trailing commas, you can build up options and combine them together and it's strictly enforces what can be passed in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment