Functions become first-class citizen in Swift. That makes it possible to use functions as a variable or parameter now. Furthermore, we can now even define a new function in a function. This brings us a new possibility of controlling code access level and structure.
Think about how many times we split a long method into several small pieces. Even these pieces would be used only once, they exist in the scope of a class. In fact, these short split methods have nothing to do with the class concept, and are not behaviors of the current class. If we follow a strict object orientation programming, they should not appear at all. On the other hand, these small private functions are laid in the same level with other public methods of the class, which makes no sense for the readers to make clear the structure of your code. All class code is flattened without distinction. It is too bad.
Now Swift comes to save it. We can define a function in another function now. That means that it is possible to make the functions nesting.
Take a Request
class in network for example. A common task when request with a url is encoding parameters into the url string. The input may be consisted by a single value, arrays, dictionaries or even the combination of them. To make the code short and easy to read, we may write like this:
func appendQuery(var url: String,
key: String,
value: AnyObject) -> String {
if let dictionary = value as? [String: AnyObject] {
return appendQueryDictionary(url, key, dictionary)
} else if let array = value as? [AnyObject] {
return appendQueryArray(url, key, array)
} else {
return appendQuerySingle(url, key, value)
}
}
func appendQueryDictionary(var url: String,
key: String,
value: [String: AnyObject]) -> String {
//...
return result
}
func appendQueryArray(var url: String,
key: String,
value: [AnyObject]) -> String {
//...
return result
}
func appendQuerySingle(var url: String,
key: String,
value: AnyObject) -> String {
//...
return result
}
In fact appendQueryDictionary
, appendQueryArray
and appendQuerySingle
will only be called from the appendQuery
function. There is no need for Request
class to know these three methods. It is sufficient to use appendQuery
only and let it handle any input types.
So it would be more reasonable to put them into appendQuery
to get a better version:
func appendQuery(var url: String,
key: String,
value: AnyObject) -> String {
func appendQueryDictionary(var url: String,
key: String,
value: [String: AnyObject]) -> String {
//...
return result
}
func appendQueryArray(var url: String,
key: String,
value: [AnyObject]) -> String {
//...
return result
}
func appendQuerySingle(var url: String,
key: String,
value: AnyObject) -> String {
//...
return result
}
if let dictionary = value as? [String: AnyObject] {
return appendQueryDictionary(url, key, dictionary)
} else if let array = value as? [AnyObject] {
return appendQueryArray(url, key, array)
} else {
return appendQuerySingle(url, key, value)
}
}
Another use case for nested function would be in the protection level in Swift. There are three access protection levels: public
, internal
and private
. private
provides us the lowest accessibility - just in the current file. But sometime it is not what we want. We can use nested function to limit a function in a function scope, which is always smaller than the file scope. An example was already given in Parameter Type of Function, incrementor
should not be known except to the parent function.
func makeIncrementor(addNumber: Int) -> ((inout Int) -> Void) {
func incrementor(inout variable: Int) -> Void {
variable += addNumber;
}
return incrementor;
}