What is the correct indentation between directives such as `#if`?

I've been annoyed by Xcode lately since I had to use a few more #if directives than usual. Xcode always wants no indentation between directives such as #if.

#if DEBUG
foo() // Xcode's preferred style
#endif

I tend to write:

#if DEBUG
  foo()
#endif

control + i shortcut in Xcode always messes up the whole indentation (not only for directives).


So I'm asking for clarification from the community. What is the correct or preferred indentation for such code?

3 Likes

I prefer to have Xcode treat directives as if they were regular language expressions, and indent them the same way:

#if os(Linux)
    typealias PlatformType = LinuxType
#else
    typealias PlatformType = DarwinType
#endif

class MyClass {
    var prop: PlatformType

    init(prop: PlatformType) { ... }
}
8 Likes

I wish that Xcode was also open-source so that the community could provide fixes to such small annoyances which probably are very low priority for the Xcode team itself.

5 Likes

I tend to do what you guys do, too.

3 Likes

I have gone back and forth over the years and settled on keeping them inline in order to reserve indentation to indicate a change in scope (ie: a closure, function, new type, if-else/switch, etc).

I do see the benefits of either method though, which is why I wavered between them over the years.

2 Likes

I tend to not indent the directives, and leave the body matching the enclosing indentation, to show what it'll look like after the #if has been folded away. That's probably a holdover from C-land, though, where #if happens so "early on" that you can stick half a statement on one side and half on the other and the compiler says "sure, okay".

6 Likes

I do the same, basically for the same reasons. This does make me think about the way availability checks work though. These are used in patterns which create scopes, but you get a compile-time guarantee of which scope will actually be entered at run time.

if #available(iOS 10.0, *) {
    doStuff(with: someModernSymbol)
} else {
    doEquivalentStuff(with: someFallbackSymbol)
}

I think I would actually prefer if this worked the same way as #if to be honest (would let you use availability checks in patterns where, syntactically, they currently can't be). But I'm sure there are reasons for it to be like this.

if #available is a run-time check, not a compile-time check. That's why it's spelled differently from #if.

EDIT: To be clear, I'm not contradicting you. It's a run-time check that gives you a compile-time guarantee.

1 Like

Fun Swift indentation excercise:

  1. Add this to a swift project:
func foo() {
    let bar = [
        "baz",
        "blee"
    ]
}
  1. Hit control+i to indent.

Looks alright, doesn't it?

So now go ahead and:

  1. Add a trailing comma after "blee":

  2. Do the indentation dance once more:

func foo() {
    let bar = [
        "baz",
        "blee",
        ]
}

:man_shrugging:t2:

Another formatting pattern I use frequently that makes Xcode barf:

func foo(
    bar: Int,
    baz: Int,
    blee: Int
    ) { // šŸ‘ˆšŸ» šŸ¤¬
    // stuff
}

Why do you do this to me, Xcode? Why do you hate me?

5 Likes

There more such things. Some of them happen when you type the # character. Xcode will also completely break multiline formatted JSON strings. guard else is never indented correctly.

1 Like

You should file bugs for those at bugreport.apple.com under "Developer Tools/Xcode", but it's super annoying it takes so much effort and time for so simple "bugs" :frowning:

2 Likes

I recently filed a bug about how Xcodeā€™s automatic indentation strips the leading whitespace from all lines of a multiline string literal, if thatā€™s what youā€™re referring to. It was marked as a duplicate of rdar://problem/20193017

Someone can correct me if I'm wrong, but Xcode uses the open source Swift toolchain to format Swift code.

For example, the issue with the trailing comma should be fixed already (included in Swift 5): https://github.com/apple/swift/pull/18231

FWIW, the SwiftFormat Xcode editor extension can be configured to indent #if blocks according to your preference (by default it treats them like a normal scope, as you do): GitHub - nicklockwood/SwiftFormat: A command-line tool and Xcode Extension for formatting Swift code

I appreciate your feedback but the mentioned issues here are really not questioning what other tooling we can make use of to fix/workaround them.