Home > Software design >  Is there a way to pass a custom flag to swift on command line and test for it in source?
Is there a way to pass a custom flag to swift on command line and test for it in source?

Time:10-21

In C it's easy to pass a custom flag on the command line and test for it in a source file, like this:

gcc ... -DMY_CUSTOM_FLAG source.c
// source.c
#ifdef MY_CUSTOM_FLAG
// ...
#else
// ...
#endif

Is there a way to do the equivalent in swift?

If not, is there a way to make my source code do something different depending on whether it's being edited in Visual Studio Code (or being parsed by sourcekit-lsp etc) or built as an xcode project for iOS?

Ultimately, what I'm trying to achieve is the ability to do the bulk of my code editing in VS Code despite this being an iPhone app. VS Code only supports Swift Package Manager, but iOS apps can't be 100% SPM. VS Code can easily be tricked into thinking the project is a package for editing purposes by providing a Package.swift file, while still using traditional xcode project files in xcode for building and previews. The snag I've hit is that my app uses Firebase, so I need a tiny bit of ObjC code (a function called tryObjC) to catch NSExceptions and repackage them as a return value, otherwise it crashes whenever I try to run a preview. SPM doesn't support mixed ObjectiveC and Swift, so I'm looking for a simple way around this, bearing in mind that for editing purposes tryObjC doesn't need to be the real deal so could be replaced by a dummy version written in Swift.

CodePudding user response:

You can do basically same thing

swift -D MY_CUSTOM_FLAG ...

There is a space after the -D. This sets MY_CUSTOM_FLAG to true, which otherwise it would be assumed to be false:

#if MY_CUSTOM_FLAG
// code for when MY_CUSTOM_FLAG is defined
#else
// code for when MY_CUSTOM_FLAG is not defined
#endif

AFAIK, you can't set it to some arbitrary value like you could do in C/C /Obj-C. You just define that the compile-time symbol exists, which makes it evaluate to true.

Just remember that Swift doesn't have a preprocessor. It supports compiler directives, which may seem like a minor distinction, but it means there are lots of things the C preprocessor will do that you can't do with Swift compiler directives, and in Swift even the portions of #if statements that aren't compiled still have to be syntactically correct. They don't undergo type-checking or any semantic analysis, but they are checked for syntax. It also keeps you from doing some of the things you might do in C that it would seem like you could in Swift.

let defaultSettings: [String: String] =
[
    "theme": "dark",
    #if MY_CUSTOM_FLAG
    "highlightMouse": "on", // <-- Error
    #endif
]

In that example you'd have to define two dictionaries. I'd do something like this to avoid duplication

fileprivate let commonDefaults: [String: String] =
[
    "theme": "dark",
    // maybe other common settings
]

#if MY_CUSTOM_FLAG 
fileprivate let customFlagDefaults: [String: String] =
[
    "highlightMouse": "on",
    // Maybe other settings
]
#else
fileprivate let customFlagDefaults: [String: String] = [:]
#endif

let defaultSettings = commonDefaults   customFlagDefaults
  • Related