10

In a class, I defined a private constant, I try to use the constant as a default value of parameter of a function:

class Foo { // instance variable private let DefaultValue = 10 // Compiler error: Cannot use instance member 'DefaultValue' as a default parameter public func doTask(amount: Int = DefaultValue) { ... } } 

But I get compiler error: Cannot use instance member 'DefaultValue' as a default parameter.

Then, I also tried declare DefaultValue as private static:

class Foo { // static variable private static let DefaultValue = 10 // Compiler error: Static let 'DefaultValue' is private and cannot be referenced from a default argument value public func doTask(amount: Int = DefaultValue) { ... } } 

But I get new compiler error: Static let 'DefaultValue' is private and cannot be referenced from a default argument value

I need to keep DefaultValue private to this class & I would like to assign default value to the parameter of function with a private variable, whether this is achievable in Swift 4?

3
  • Why does DefaultValue need to remain private? In Swift 5, default argument expressions are now shown in a module's generated interface, so default arguments must only refer to public things (compare stackoverflow.com/q/46524232/2976878) Commented Sep 18, 2018 at 19:05
  • @Hamish: Duplicate? Commented Sep 18, 2018 at 19:07
  • 1
    @MartinR Hmm, your answer more directly answers OP's question, so I'd say keep it open. Commented Sep 18, 2018 at 19:10

4 Answers 4

13

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 // ... } } 
Sign up to request clarification or add additional context in comments.

Comments

1

A somewhat complicated, but workable, solution, for hiding the default value you want within your class is to use a protocol and a conforming struct whose intimate details are known only by the file declaring Foo:

// enabling custom stuff public protocol IntLike { var intValue: Int { get } } // allowing to pass Int's extension Int: IntLike { public var intValue: Int { return self } } public class Foo { // the placeholder public struct FooIntLike: IntLike { // what we really want to hide fileprivate let realInt = 10 public init() { } public var intValue: Int = Int.max // or 0, or whatever } public func doTask(amount: IntLike = FooIntLike()) { // default value will expand to a non-nil value for `realInt` let amount = (amount as? FooIntLike)?.realInt ?? amount.intValue // do your stuff with the amount } } 

Callers of doTask are able to pass Int's, while not knowing what the default value provides.

5 Comments

Wouldn't Foo.FooIntLike().realInt reveal the “secret default” as well?
@MartinR realInt is fileprivate, which makes it unavailable for callers that don't have access to the source code (e.g. framework clients). If the callers would have access to the source code, then there would be no solution for the problem in the question.
You are right. But print(Foo.FooIntLike()) prints FooIntLike(realInt: 10, intValue: 9223372036854775807), so you may need to override the description method as well
@MartinR valid point, however wondering if the description would be the same if called from an external context, I doubt that Swift would allow so easy leakage of private details.
I tested that only with a separate source file, not with a separate framework. So yes, that case might be different.
1

In cases where using nil might not be immediately obvious and the task is either small or easily placed within a private function, just adding another signature works for me.

You could even change it to doTaskWithDefaultAmount() depending on how important it is that the user know about this default value.

class Foo { public func doTask() { calculateStuff(from: self.defaultValue) } public func doTask(amount: Int) { calculateStuff(from: amount) } } 

Comments

-1

How about a top-level variable?

fileprivate let DefaultValue = 10 class Foo { public func doTask(amount: Int = DefaultValue) { ... } } 

1 Comment

Note that this won't compile if class Foo is public.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.