Compared to Objective-C, Swift has a lot of extra options for what type of parameters can be passed in. Here are some examples.
In Swift, there is a new concept of optional types:
Optionals say either “there is a value, and it equals x” or “there isn’t a value at all.” Optionals are similar to using nil with pointers in Objective-C, but they work for any type, not just classes. Optionals are safer and more expressive than nil pointers in Objective-C and are at the heart of many of Swift’s most powerful features.
To indicate that a parameter type is optional (can be nil), just add a question mark after the type specification:
| func myFuncWithOptionalType(parameter: String?) { |
| // function execution |
| } |
| |
| myFuncWithOptionalType("someString") |
| myFuncWithOptionalType(nil) |
When working with optionals, don’t forget to unwrap!
| func myFuncWithOptionalType(optionalParameter: String?) { |
| if let unwrappedOptional = optionalParameter { |
| println("The optional has a value! It's \(unwrappedOptional)") |
| } else { |
| println("The optional is nil!") |
| } |
| } |
| |
| myFuncWithOptionalType("someString") |
| // The optional has a value! It's someString |
| |
| myFuncWithOptionalType(nil) |
| // The optional is nil |
If you’re coming from Objective-C, getting used to working with optionals definitely takes some time!
| func hello(name: String = "you") { |
| println("hello, \(name)") |
| } |
| |
| hello(name: "Mr. Roboto") |
| // hello, Mr. Roboto |
| |
| hello() |
| // hello, you |
Note that a parameter with a default value automatically has an external parameter name.
And since parameters with a default value can be skipped when the function is called, it is best practice to put all your parameters with default values at the end of a function’s parameter list. Here is a note from the The Swift Programming Language on the topic:
Place parameters with default values at the end of a function’s parameter list. This ensures that all calls to the function use the same order for their non-default arguments, and makes it clear that the same function is being called in each case.
I’m a huge fan of default parameters, mostly because it makes code easy to change and backward-compatible. You might start out with two parameters for your specific use case at the time, such as a function to configure a custom UITableViewCell. And you can add a new parameter with a default value if another use case comes up that requires another parameter, such as a different text color for your cell’s label. All the other places where this function has already been called will be fine, and the new part of your code that needs the parameter can just pass in the non-default value!
Variadic parameters are simply a more readable version of passing in an array of elements. In fact, if you were to look at the type of the internal parameter names in the following example, you’d see that it is of type [String] (array of strings):
| func helloWithNames(names: String...) { |
| for name in names { |
| println("Hello, \(name)") |
| } |
| } |
| |
| // 2 names |
| helloWithNames("Mr. Robot", "Mr. Potato") |
| // Hello, Mr. Robot |
| // Hello, Mr. Potato |
| |
| // 4 names |
| helloWithNames("Batman", "Superman", "Wonder Woman", "Catwoman") |
| // Hello, Batman |
| // Hello, Superman |
| // Hello, Wonder Woman |
| // Hello, Catwoman |
The catch here is to remember that it is possible to pass in 0 values, just like it is possible to pass in an empty array, so don’t forget to check for the empty array if needed:
| func helloWithNames(names: String...) { |
| if names.count > 0 { |
| for name in names { |
| println("Hello, \(name)") |
| } |
| } else { |
| println("Nobody here!") |
| } |
| } |
| |
| helloWithNames() |
| // Nobody here! |
Another note about variadic parameters: the variadic parameter must be the last parameter in your function’s parameter list!
With inout parameters, you have the ability to manipulate external variables (in other words, pass by reference):
| var name1 = "Mr. Potato" |
| var name2 = "Mr. Roboto" |
| |
| func nameSwap(inout name1: String, inout name2: String) { |
| let oldName1 = name1 |
| name1 = name2 |
| name2 = oldName1 |
| } |
| |
| nameSwap(&name1, &name2) |
| |
| name1 |
| // Mr. Roboto |
| |
| name2 |
| // Mr. Potato |
This is a very common pattern in Objective-C for handling error scenarios. NSJSONSerialization is just one example:
| - (void)parseJSONData:(NSData *)jsonData |
| { |
| NSError *error = nil; |
| id jsonResult = |
| [NSJSONSerialization JSONObjectWithData:jsonData |
| options:0 error:&error]; |
| |
| if (!jsonResult) { |
| NSLog(@"ERROR: %@", error.description); |
| } |
| } |
Since Swift is so new, there aren’t clear conventions on handling errors just yet, but there are definitely a lot of options beyond inout parameters! Take a look at David Owen’s recent blog post on error handling in Swift.[32]
I’m not going to get too much into generics in this chapter, but here’s a very simple example for how you can make a function accept parameters of different types while making sure that both parameters are of the same type:
| func valueSwap<T>(inout value1: T, inout value2: T) { |
| let oldValue1 = value1 |
| value1 = value2 |
| value2 = oldValue1 |
| } |
| |
| var name1 = "Mr. Potato" |
| var name2 = "Mr. Roboto" |
| |
| valueSwap(&name1, &name2) |
| |
| name1 // Mr. Roboto |
| name2 // Mr. Potato |
| |
| var number1 = 2 |
| var number2 = 5 |
| |
| valueSwap(&number1, &number2) |
| |
| number1 // 5 |
| number2 // 2 |
By default, parameters that are passed into a function are constants, so they cannot be manipulated within the scope of the function. If you would like to change that behavior, just use the var keyword for your parameters:
| var name = "Mr. Roboto" |
| |
| func appendNumbersToName(var name: String, #maxNumber: Int) -> String { |
| for i in 0..<maxNumber { |
| name += String(i + 1) |
| } |
| return name |
| } |
| |
| appendNumbersToName(name, maxNumber:5) |
| // Mr. Robot12345 |
| |
| name |
| // Mr. Roboto |
Note that this is different than an inout parameter—variable parameters don’t change the external passed-in variable!
In Swift, functions can be passed around just like variables. For example, a function can have another function passed in as a parameter:
| func luckyNumberForName(name: String, #lotteryHandler: (String, Int) -> |
| String) -> String { |
| let luckyNumber = Int(arc4random() % 100) |
| return lotteryHandler(name, luckyNumber) |
| } |
| |
| func defaultLotteryHandler(name: String, luckyNumber: Int) -> String { |
| return "\(name), your lucky number is \(luckyNumber)" |
| } |
| |
| luckyNumberForName("Mr. Roboto", lotteryHandler: defaultLotteryHandler) |
| // Mr. Roboto, your lucky number is 38 |
Note that only the function reference gets passed in—defaultLotteryHandler, in this case. The function gets executed later as decided by the receiving function.
Instance methods can also be passed in a similar way:
| func luckyNumberForName(name: String, #lotteryHandler: (String, Int) -> |
| String) -> String { |
| let luckyNumber = Int(arc4random() % 100) |
| return lotteryHandler(name, luckyNumber) |
| } |
| |
| class FunLottery { |
| |
| func defaultLotteryHandler(name: String, luckyNumber: Int) -> String { |
| return "\(name), your lucky number is \(luckyNumber)" |
| } |
| |
| } |
| |
| let funLottery = FunLottery() |
| luckyNumberForName("Mr. Roboto", |
| lotteryHandler: funLottery.defaultLotteryHandler) |
| // Mr. Roboto, your lucky number is 38 |
To make your function definition a bit more readable, consider type-aliasing your function (similar to typedef in Objective-C):
| typealias lotteryOutputHandler = (String, Int) -> String |
| |
| func luckyNumberForName(name: String, #lotteryHandler: lotteryOutputHandler) -> |
| String { |
| let luckyNumber = Int(arc4random() % 100) |
| return lotteryHandler(name, luckyNumber) |
| } |
You can also have a function without a name as a parameter type (similar to blocks in Objective-C):
| func luckyNumberForName(name: String, #lotteryHandler: (String, Int) -> |
| String) -> String { |
| let luckyNumber = Int(arc4random() % 100) |
| return lotteryHandler(name, luckyNumber) |
| } |
| |
| luckyNumberForName("Mr. Roboto", lotteryHandler: {name, number in |
| return "\(name)'s' lucky number is \(number)" |
| }) |
| // Mr. Roboto's lucky number is 74 |
In Objective-C, using blocks as parameters is popular for completion and error handlers in methods that execute an asynchronous operation. This should continue to be a popular pattern in Swift as well.