Swift: function with default parameter before non-default parameter
The current compiler does allow default parameters in the middle of a parameter list.
You can call the function like this if you want to use the default value for the first
parameter:
f(1)
If you want to supply a new value for the first
parameter, use its external name:
f(first: 3, 1)
The documentation explains that parameters with a default value are automatically given an external name:
Swift provides an automatic external name for any defaulted parameter you define, if you do not provide an external name yourself. The automatic external name is the same as the local name, as if you had written a hash symbol before the local name in your code.
Default optional parameter in Swift function
Optionals and default parameters are two different things.
An Optional is a variable that can be nil
, that's it.
Default parameters use a default value when you omit that parameter, this default value is specified like this: func test(param: Int = 0)
If you specify a parameter that is an optional, you have to provide it, even if the value you want to pass is nil
. If your function looks like this func test(param: Int?)
, you can't call it like this test()
. Even though the parameter is optional, it doesn't have a default value.
You can also combine the two and have a parameter that takes an optional where nil
is the default value, like this: func test(param: Int? = nil)
.
Why aren't default parameters for functions respected when called via Selector in Swift?
Default parameters are inserted by the Swift compiler when you call the function.
So this is a compile-time thing.
When manually calling the function via a selector, you use the Objective-C runtime, which has no idea about your default parameters.
This is a runtime thing.
Moreover, default parameters don't exist in Objective-C.
You can think of Swift default parameters as a compile-time convenience.
But once you run your code, they're basically gone.
EDIT
If you're looking for a workaround, I can suggest having two different functions, without using default parameters:
@objc func printValue()
{
printValue(value: 7)
}
@objc func printValue(value:Int)
{}
This way, you can call it without arguments through the Objective-C runtime.
Note that when using #selector
, you'll need to cast to resolve ambiguity:
self.button?.addTarget(self, action: #selector((printValue as () -> Void)), for: .touchUpInside)
When are Swift function default parameter values evaluated?
From The Swift Programming Language, under Language Reference > Declarations > Special Kinds of Parameters:
A parameter with an equals sign (=) and an expression after its type is understood to have a default value of the given expression. The given expression is evaluated when the function is called. If the parameter is omitted when calling the function, the default value is used instead.
You can demonstrate this for yourself by putting the following in a playground:
import Foundation
func foo(i: UInt32 = arc4random()) {
print(i)
}
foo()
foo()
foo()
foo()
foo()
which will print five different random numbers (unless the random number generator generates the same number five times by some astronomically improbable coincidence).
It's not explicit in the docs quoted above, so it's worth noting that when you do specify the argument when calling the function the default expression is not evaluated. You can demonstrate that in a playground too:
func getSomeInt() -> Int {
print("getSomeInt() was called")
return 42
}
func bar(_ i: Int = getSomeInt()) {
print(i)
}
bar(1)
and when that runs you'll see that "getSomeInt() was called" is not printed.
Using a property as a default parameter value for a method in the same class
I don't think you're doing anything wrong.
The language specification only says that a default parameter should come before non-default parameters (p169), and that the default value is defined by an expression (p637).
It does not say what that expression is allowed to reference. It seems like it is not allowed to reference the instance on which you are calling the method, i.e., self, which seems like it would be necessary to reference self.niceAnimal.
As a workaround, you could define the default parameter as an optional with a default value of nil, and then set the actual value with an "if let" that references the member variable in the default case, like so:
class animal {
var niceAnimal: Bool
var numberOfLegs: Int
init(numberOfLegs: Int, animalIsNice: Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(numberOfLegs: Int, animalIsNice: Bool? = nil) {
if let animalIsNice = animalIsNice ?? self.niceAnimal {
// print
}
}
}
Implementing a function with a default parameter defined in a protocol
This is due to the fact that the call
castedCar.move(to: CGPoint(x: 20, y: 10))
is able to be resolved to the protocol requirement func move(to point: CGPoint)
– therefore the call will be dynamically dispatched to via the protocol witness table (the mechanism by which protocol-typed values achieve polymorphism), allowing Car
's implementation to be called.
However, the call
castedCar.move()
does not match the protocol requirement func move(to point: CGPoint)
. It therefore won't be dispatched to via the protocol witness table (which only contains method entries for protocol requirements). Instead, as castedCar
is typed as Movable
, the compiler will have to rely on static dispatch. Therefore the implementation in the protocol extension will be called.
Default parameter values are merely a static feature of functions – only a single overload of the function will actually be emitted by the compiler (one with all the parameters). Attempting to apply a function by excluding one of its parameters which has a default value will trigger the compiler to insert an evaluation of that default parameter value (as it may not be constant), and then insert that value at the call site.
For that reason, functions with default parameter values simply do not play well with dynamic dispatch. You can also get unexpected results with classes overriding methods with default parameter values – see for example this bug report.
One way to get the dynamic dispatch you want for the default parameter value would be to define a static
property requirement in your protocol, along with a move()
overload in a protocol extension which simply applies move(to:)
with it.
protocol Moveable {
static var defaultMoveToPoint: CGPoint { get }
func move(to point: CGPoint)
}
extension Moveable {
static var defaultMoveToPoint: CGPoint {
return .zero
}
// Apply move(to:) with our given defined default. Because defaultMoveToPoint is a
// protocol requirement, it can be dynamically dispatched to.
func move() {
move(to: type(of: self).defaultMoveToPoint)
}
func move(to point: CGPoint) {
print("Moving to origin: \(point)")
}
}
class Car: Moveable {
static let defaultMoveToPoint = CGPoint(x: 1, y: 2)
func move(to point: CGPoint) {
print("Moving to point: \(point)")
}
}
let castedCar: Moveable = Car()
castedCar.move(to: CGPoint(x: 20, y: 10)) // Moving to point: (20.0, 10.0)
castedCar.move() // Moving to point: (1.0, 2.0)
Because defaultMoveToPoint
is now a protocol requirement – it can be dynamically dispatched to, thus giving you your desired behaviour.
As an addendum, note that we're calling defaultMoveToPoint
on type(of: self)
rather than Self
. This will give us the dynamic metatype value for the instance, rather than the static metatype value of what the method is called on, ensuring defaultMoveToPoint
is dispatched correctly. If however, the static type of whatever move()
is called on (with the exception of Moveable
itself) is sufficient, you can use Self
.
I go into the differences between the dynamic and static metatype values available in protocol extensions in more detail in this Q&A.
Is it possible to have parameters that aren't used as arguments?
You (everyone, really) would really benefit from reading the Swift book, cover to cover.
What you're looking for is called a default value.
func test(paramA: String = "Your default value here") {}
or
func test(paramA: String? = nil) {}
The former is simpler, but more limited. For example, you can't distinguish rather the default value "Your default value here"
was used, or whether the caller passed in their own value, which happens to be "Your default value here"
). In my experience, the distinction is seldom required, but it's good to call out just in case.
In the latter case, you have the flexibility to handle the optional in many more ways. You could substitute a default value with ??
, do conditional binding, map
it, etc.
Default Parameter Value for Function Type as Parameter Type in Swift 3
First of all, a question should provide a code block as specific as possible.
The question
So, you have a function foo
which accepts a parameter of type closure and you want to provide a default value
right?
The solution
Here's the code
func foo(completion: ()->() = { _ in print("Default completion") }) {
completion()
}
Testing
Now you can call foo
passing your own closure
foo { print("Hello world") } // Hello world
Or using using the default param
foo() // Default completion
Using a class' method as default parameter in Swift
In
func operate(n1: Int, n2: Int, _ f: ((Int, Int) -> Int)? = add) -> Int
add
is evaluated as Calculator.add
which has the type
(Calculator) -> (Int, Int) -> Int
as explained in Instance Methods are “Curried” Functions in Swift.
One option is to use nil
as the default value (since you do optional chaining anyway later):
class Calculator {
func operate(n1: Int, n2: Int, _ f: ((Int, Int) -> Int)? = nil) -> Int {
return (f ?? add)(n1, n2)
}
func add(n1: Int, n2: Int) -> Int {
return n1 + n2
}
}
Another option is to make the default function a static
method, and the function parameter non-optional:
class Calculator {
func operate(n1: Int, n2: Int, _ f: ((Int, Int) -> Int) = add) -> Int {
return f(n1, n2)
}
static func add(n1: Int, n2: Int) -> Int {
return n1 + n2
}
}
Related Topics
Using Key-Value Programming (Kvp) with Swift
How to Extract Image from Lplinkview in Linkpresentation Framework
How to Reset Hididletime on MACos 10.14
Load a Pcm into a Avaudiopcmbuffer
Making Cocoa Application Scriptable Swift
Apple MACh-O Linker Error, After Changing Project Name
Swift: Deallocate Gamescene After Transition to New Scene
Swift: How to Open File with Associated Application
Ios-Charts Library: X-Axis Labels Without Backing Data Not Showing
How to Get the Yaw, Pitch, Roll of an Aranchor in Absolute Terms
Single and Double Taps on Uitableviewcell in Swift 3
What's the Best Approach to Prefill Core Data Store When Using Nspersistentcloudkitcontainer
How Safe Are Swift Collections When Used with Invalidated Iterators/Indices
How to Check If Annotation Is Clustered (Mkmarkerannotationview and Cluster)
Guarantees About the Lifetime of a Reference in a Local Variable
Nsurlsession/Nsurlconnection Http Load Failed (Kcfstreamerrordomainssl, -9802)
Cancelling an Alamofire Request Wrapped in Nsoperation Causes Multiple Kvo