#ifdef replacement in the Swift language

前端 未结 17 1715
名媛妹妹
名媛妹妹 2020-11-22 10:35

In C/C++/Objective C you can define a macro using compiler preprocessors. Moreover, you can include/exclude some parts of code using compiler preprocessors.

         


        
相关标签:
17条回答
  • 2020-11-22 11:15

    As of Swift 4.1, if all you need is just check whether the code is built with debug or release configuration, you may use the built-in functions:

    • _isDebugAssertConfiguration() (true when optimization is set to -Onone)
    • _isReleaseAssertConfiguration() (true when optimization is set to -O) (not available on Swift 3+)
    • _isFastAssertConfiguration() (true when optimization is set to -Ounchecked)

    e.g.

    func obtain() -> AbstractThing {
        if _isDebugAssertConfiguration() {
            return DecoratedThingWithDebugInformation(Thing())
        } else {
            return Thing()
        }
    }
    

    Compared with preprocessor macros,

    • ✓ You don't need to define a custom -D DEBUG flag to use it
    • ~ It is actually defined in terms of optimization settings, not Xcode build configuration
    • ✗ Undocumented, which means the function can be removed in any update (but it should be AppStore-safe since the optimizer will turn these into constants)

      • these once removed, but brought back to public to lack of @testable attribute, fate uncertain on future Swift.
    • ✗ Using in if/else will always generate a "Will never be executed" warning.

    0 讨论(0)
  • 2020-11-22 11:16

    My two cents for Xcode 8:

    a) A custom flag using the -D prefix works fine, but...

    b) Simpler use:

    In Xcode 8 there is a new section: "Active Compilation Conditions", already with two rows, for debug and release.

    Simply add your define WITHOUT -D.

    0 讨论(0)
  • 2020-11-22 11:19

    Yes you can do it.

    In Swift you can still use the "#if/#else/#endif" preprocessor macros (although more constrained), as per Apple docs. Here's an example:

    #if DEBUG
        let a = 2
    #else
        let a = 3
    #endif
    

    Now, you must set the "DEBUG" symbol elsewhere, though. Set it in the "Swift Compiler - Custom Flags" section, "Other Swift Flags" line. You add the DEBUG symbol with the -D DEBUG entry.

    As usual, you can set a different value when in Debug or when in Release.

    I tested it in real code and it works; it doesn't seem to be recognized in a playground though.

    You can read my original post here.


    IMPORTANT NOTE: -DDEBUG=1 doesn't work. Only -D DEBUG works. Seems compiler is ignoring a flag with a specific value.

    0 讨论(0)
  • 2020-11-22 11:22

    As stated in Apple Docs

    The Swift compiler does not include a preprocessor. Instead, it takes advantage of compile-time attributes, build configurations, and language features to accomplish the same functionality. For this reason, preprocessor directives are not imported in Swift.

    I've managed to achieve what I wanted by using custom Build Configurations:

    1. Go to your project / select your target / Build Settings / search for Custom Flags
    2. For your chosen target set your custom flag using -D prefix (without white spaces), for both Debug and Release
    3. Do above steps for every target you have

    Here's how you check for target:

    #if BANANA
        print("We have a banana")
    #elseif MELONA
        print("Melona")
    #else
        print("Kiwi")
    #endif
    

    Tested using Swift 2.2

    0 讨论(0)
  • 2020-11-22 11:23

    There are some processors that take an argument and I listed them below. you can change the argument as you like:

    #if os(macOS) /* Checks the target operating system */
    
    #if canImport(UIKit) /* Check if a module presents */
    
    #if swift(<5) /* Check the Swift version */
    
    #if targetEnvironment(simulator) /* Check envrionments like Simulator or Catalyst */
    
    #if compiler(<7) /* Check compiler version */
    

    Also, You can use any custom flags like DEBUG or any other flags you defined

    #if DEBUG
    print("Debug mode")
    #endif
    
    0 讨论(0)
  • 2020-11-22 11:24

    In many situations, you don't really need conditional compilation; you just need conditional behavior that you can switch on and off. For that, you can use an environment variable. This has the huge advantage that you don't actually have to recompile.

    You can set the environment variable, and easily switch it on or off, in the scheme editor:

    enter image description here

    You can retrieve the environment variable with NSProcessInfo:

        let dic = NSProcessInfo.processInfo().environment
        if dic["TRIPLE"] != nil {
            // ... do secret stuff here ...
        }
    

    Here's a real-life example. My app runs only on the device, because it uses the music library, which doesn't exist on the Simulator. How, then, to take screen shots on the Simulator for devices I don't own? Without those screen shots, I can't submit to the AppStore.

    I need fake data and a different way of processing it. I have two environment variables: one which, when switched on, tells the app to generate the fake data from the real data while running on my device; the other which, when switched on, uses the fake data (not the missing music library) while running on the Simulator. Switching each of those special modes on / off is easy thanks to environment variable checkboxes in the Scheme editor. And the bonus is that I can't accidentally use them in my App Store build, because archiving has no environment variables.

    0 讨论(0)
提交回复
热议问题