Swift: Using Member Constant as Default Value for Function Parameter

Swift: using member constant as default value for function parameter

You have to define the default value as a static property:

class SuperDuperCoolClass : UIViewController {

static let primaryColor : UIColor = UIColor(red: 72.0/255.0, green: 86.0/255.0, blue: 114.0/255.0, alpha: 1.0)

func configureCheckmarkedBullet(bullet: UIButton, color: UIColor = primaryColor){
}
}

The above code compiles with Swift 1.2 (Xcode 6.3) which added support
for static computed properties. In earlier versions, you can define
a nested struct containing the property as a workaround (compare
Class variables not yet supported):

class SuperDuperCoolClass : UIViewController {

struct Constants {
static let primaryColor : UIColor = UIColor(red: 72.0/255.0, green: 86.0/255.0, blue: 114.0/255.0, alpha: 1.0)
}

func configureCheckmarkedBullet(bullet: UIButton, color: UIColor = Constants.primaryColor){
}
}

Difficulties to assign default value to a parameter of a function

I don't think that is possible. The default value is inserted at the calling site, and therefore needs to be public, see also
Access control in swift 4.

A possible workaround would be to make the parameter optional,
and substitute nil by the default value locally:

class Foo {
private static let DefaultValue = 10

public func doTask(amount: Int? = nil) {
let amount = amount ?? Foo.DefaultValue
// ...
}
}

Instance member as default parameter

The reason is in different context visibility, the arguments context of interface function is external in relation to class declaration, so members are not visible, but nested function is declared inside class context, as other function body, so members are visible.

So the solution might be static, as already proposed, but it might have drawbacks (eg. for reference default members), so I recommend to use it only for constants.

The other possible solutions is below

func add(value: String, node: TrieNode?) { // no error
func _add(value: String, node: TrieNode? = root) { // in-context
var myNode = node
if myNode == nil {
myNode = root
}
if value.count == 0 {
node?.setEnd()
return
} else if myNode!.keys[String(value.first!)] == nil {
myNode!.keys[String(value.first!)] = TrieNode()
return add(value: String(value.dropFirst()), node: myNode!.keys[String(value.first!)])
} else {
return add(value: String(value.dropFirst()), node: myNode!.keys[String(value.first!)])
}
}
_add(value: value, node: node)
}

Default values for function parameters where value passed in is nil

How about:

class MyObj {

var foobar:String

init(foo: String?=nil) {
self.foobar = foo ?? "hello"
}
}

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.

Add default value to inout parameter using Swfit

The two keywords you're talking about, inout and var, are very different.

From Apple Documentation:

In-out parameters are passed as follows:

  1. When the function is called, the value of the argument is copied.
  2. In the body of the function, the copy is modified.
  3. When the function returns, the copy’s value is assigned to the original argument.

Therefore you can't give a default value to an inout parameter, as it would make the inout property completely useless.

What you can do is receive a normal (constant) parameter with a default value, and declare a new var with the same name this way (code from the Swift Evolution's Removing var from Function Parameters Proposal, with the addition of the default parameter):

func foo(i: Int = 5) {
var i = i
// now you can change i as you'd like
}

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.



Related Topics



Leave a reply



Submit