For the code below:
let services: [MyServices] = [
MyService(),
#if DEBUG
DebugService(),
#endif
]
I get compiler error:
expression failed to parse: error: MyPlayground.playground:375:5: error: expected expression in container literal #if DEBUG ^
error: MyPlayground.playground:376:19: error: consecutive statements on a line must be separated by ';' DebugService(), ^ ;
error: MyPlayground.playground:376:19: error: expected expression DebugService(), ^
error: MyPlayground.playground:378:1: error: expected expression ]
But the same code works in Objective-C
NSArray *array = @[
@"11",
#if DEBUG
@"debug",
#endif
@"22"
];
Is this a compiler bug or expected behaviour ? Thank you.
CodePudding user response:
Generally the body of each clause in an #if ... #else ... #endif
compiler directive must surround complete statements.
But with the implementation of SE-308 #if
for postfix member expressions in Swift 5.5 this directive has been expanded to be able to surround postfix member expressions.
So with a custom extension
extension Array {
func appending(_ e: Element) -> Self {
return self [e]
}
}
one can conditionally initialize an array like this:
let services = [
MyService() ]
#if DEBUG
.appending(DebugService())
#endif
CodePudding user response:
The #if
/ #endif
constructs are not preprocessor macros like in C. They are compiler directives. They are part of the language.
In C, it runs the preprocessor on your source file, which results in a modified source file. That modified source file gets compiled.
Not so in Swift. In Swift, the compiler parses the source file and attempts to compile it.
Edit:
To understand why what you tried doesn't work, think of the #if / #endif
like a regular if statement. Would the following compile?
let debug = true
let services: [MyServices] = [
MyService(),
if debug {
DebugService(),
}
]
(Answer: No. That's not legal. You can't insert an if statement in the middle of the parameters of an array initializer. Same goes for an #if / #elseif
statement.)
You can’t use an #if / #endif
block to assemble fragments of a statement together.
You would have to create complete statements in both cases:
#if debug
let services: [MyServices] = [
MyService(),
DebugService(),
]
#else
let services: [MyServices] = [
MyService()
]
#endif
(Or you could make services
a var, and append DebugServices()
to it inside an #if / #endif
.)
Edit #2:
Better yet, use a "postfix" .appending()
as in @MartinR's excellent answer.
CodePudding user response:
Swift 5.5
let services: [MyServices] = [
#if DEBUG
DebugService()
#else
MyService()
#endif
]