• Swift Code Formatters
  • Original author: Mattt
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: iWeslie
  • Proofread by: Swants, FireairForce

I just left a trendy cafe. There were plenty of iOS developers whispering to each other about how they couldn’t wait for Apple to release its official style guide and formatter for Swift.

For the past few days, the community has been talking about Tony Allevato and Dave Abrahams adopting the official Swift formatting tool.

Dozens of community members have weighed in on the draft proposal. As with all style issues, everyone has a different opinion. Fortunately, however, the discourse from the community is often representative and insightful, articulating a wide variety of perspectives, use cases, and concerns.

At the time of writing, it seems that many but not all respondents approve of official formatting. Those who favor formatting constraints on code also want a tool that automatically diagnoses and fixes code that doesn’t conform to those constraints. However, others have concerns about the applicability and configurability of this format.

This week on NSHipster, we’ll talk about the Swift formatters currently available, including the Swift-format tool released as part of the proposal, and see how they work. Then we’ll weigh the pros and cons.

First, let’s start with the following question:

What is code formatting?

We define code formatting as any changes made to the code to make it easier to understand the content of the code without changing its behavior. While this definition extends to differences in equivalent forms (e.g. [Int] and Array

), we’ll limit our discussion here to Spaces and punctuation.

Like many other programming languages, Swift is very liberal in accepting newlines, tabs, and Spaces. Most whitespace is insignificant and has no effect on the code from the compiler’s point of view.

Spaces are used as an auxiliary symbol when we use them to make code easier to understand without changing its lines. The main symbol, of course, is the code itself.

Another auxiliary notation, which we discussed in our previous NSHipster article, is syntax highlighting.

While you can write almost anything on a single line of code with a semicolon, wouldn’t it be a more understandable and intuitive way to align code with white space and line breaks, all else being equal?

Unfortunately, ambiguities caused by the compiler’s acceptance of whitespace often lead to confusion and disagreement among programmers about code: “Should I add line breaks before braces or not? How do I decompose statements that are beyond the width of the editor?”

A company will often have its own set of code specifications, but they are often unclear, not enforceable, and may be outdated. The purpose of a code formatter is to automatically enforce a set of conventions so that programmers can put aside differences and focus on solving real problems.

Comparison of Formatting tools

The Swift community has been thinking about code formatting since its inception. Code writing specifications have been around since Swift’s inception, and various open source tools can automate formatting code through specifications.

You can look at the following four tools to see the current status of the Swift code formatter:

project The warehouse link
SwiftFormat Github.com/nicklockwoo…
SwiftLint Github.com/realm/Swift…
Prettier with Swift Plugin Github.com/prettier/pr…
Swift-format (proposed) Github.com/google/swif…

For brevity, this article discusses only some of the Swift formatting tools available. Learn more about: Swimat, SwiftRewriter and SwifTFMT if you’re interested.

To facilitate comparison, we designed the following code to evaluate each tool using their default configuration:

struct ShippingAddress : Codable  {
    var recipient: String
    var streetAddress : String
    var locality :String
    var region   :String;var postalCode:String
    var country:String

    init(recipient: String,        streetAddress: String,
         locality: String,region: String,postalCode: String,country:String)
    {
        self.recipient = recipient
        self.streetAddress = streetAddress
        self.locality  = locality
        self.region        = region;self.postalCode=postalCode
        guard country.count= =2, country == country.uppercased() else { fatalError("invalid country code")}self.country=country}}

let applePark = ShippingAddress(recipient:"Apple, Inc.", streetAddress:"1 Apple Park Way", locality:"Cupertino", region:"CA", postalCode:"95014", country:"US")
Copy the code

Although code formatting involves a variety of possible syntaxal and semantic transformations, we’ll focus on line breaks and indentation, which we consider fundamental requirements for any code formatter.

Admittedly, the performance benchmarks in this article are not very strict. But they should serve as a reference. We measured time in seconds using a 2017 MacBook Pro with a 2.9ghz Intel Core I7 processor and 16GB 2133 MHz LPDDR3 ram.

SwiftFormat

The first is SwiftFormat, whose introduction is a very useful tool.

The installation

SwiftFormat can be installed through Homebrew, Mint and CocoaPods.

You can install it by using the following command:

$ brew install swiftformat
Copy the code

In addition, SwiftFormat provides an Xcode extension that you can use for formatting within Xcode. Or if you are a VSCode user, you can use this plug-in.

use

The swiftformat command formats all Swift files found in the specified file and directory paths.

$ swiftformat Example.swift
Copy the code

SwiftFormat has many rules that you can configure individually via command-line options or using configuration files.

Sample output

After running SwiftFormat with the default configuration, you get the following output:

/ / swiftformat version 0.39.5
struct ShippingAddress: Codable {
    var recipient: String
    var streetAddress: String
    var locality: String
    var region: String; var postalCode: String
    var country: String

    init(recipient: String, streetAddress: String,
         locality: String, region: String, postalCode: String, country: String) {
        self.recipient = recipient
        self.streetAddress = streetAddress
        self.locality = locality
        self.region = region; self.postalCode = postalCode
        guard country.count= =2, country == country.uppercased() else { fatalError("invalid country code")}self.country = country
    }
}

let applePark = ShippingAddress(recipient: "Apple, Inc.", streetAddress: "1 Apple Park Way", locality: "Cupertino", region: "CA", postalCode: "95014", country: "US")
Copy the code

As you can see, formatting is a significant improvement over the original. Each line is indented according to its range, and statements between punctuation marks have consistent spacing. Semicolons in property declarations and line breaks in initialization parameters are retained. However, the braces were not moved to a single line as expected, and Nick fixed it at 0.39.5.

performance

SwiftFormat was consistently the fastest of the tools tested in this article, completing processing in milliseconds.

$ time swiftformat Example.swift
        0.03 real         0.01 user         0.01 sys
Copy the code

SwiftLint

Next up is SwiftLint, which is the backbone of the Swift open source community. With over 100 rules, SwiftLint can perform all sorts of checks on your code, from prioritizing AnyObject over Class (a protocol for classes only) to so-called “Yoda conditionals”, It states that the variable should be placed to the left of the comparison operator (that is, if n == 42 instead of if 42 == n).

As the name suggests, SwiftLint is not primarily a code formatter; it is really a diagnostic tool for identifying abusive conventions and misuse of apis. It is often used to format code because of its auto-correction capabilities.

The installation

You can install Homebrew using the following command:

$ brew install swiftlint
Copy the code

Or you can install it through CocoaPods, Mint, or a standalone PKG installation package.

use

To format code with SwiftLint, run the autocorrect subcommand, with the –format argument being the file or directory you want to execute.

$ swiftlint autocorrect --format --path Example.swift
Copy the code

Sample output

Run the above command and you will get the following output:

/ / swiftlint version 0.31.0
struct ShippingAddress: Codable {
    var recipient: String
    var streetAddress: String
    var locality: String
    var region: String;var postalCode: String
    var country: String

    init(recipient: String, streetAddress: String,
         locality: String, region: String, postalCode: String, country: String) {
        self.recipient = recipient
        self.streetAddress = streetAddress
        self.locality  = locality
        self.region        = region;self.postalCode=postalCode
        guard country.count= =2, country == country.uppercased() else { fatalError("invalid country code")}self.country=country}}

let applePark = ShippingAddress(recipient: "Apple, Inc.", streetAddress: "1 Apple Park Way", locality: "Cupertino", region: "CA", postalCode: "95014", country: "US")
Copy the code

SwiftLint cleans up the worst indentation and in-line spacing problems, while preserving other irrelevant Spaces. It is important to note that formatting is not the primary role of SwiftLint; it is generally incidental to providing actionable code diagnostics. From the point of view of “first of all, there are no side effects”, you don’t have to complain about the results of its treatment here.

performance

Everything that SwiftLint checks is very efficient, in our example it takes a fraction of a second.

$ time swiftlint autocorrect --quiet --format --path Example.swift
        0.11 real         0.05 user         0.02 sys
Copy the code

Swift Plugin beautification

If you haven’t used JavaScript (as I did in last week’s article), this is probably the first article you’ve heard about Prettier. Conversely, if you’re immersed in the world of ES6, React, and WebPack, you’ll almost certainly rely heavily on it.

Prettier is unique among code formatters because it represents the beauty of code, breaking it off with line breaks as if writing poetry.

Thanks to a plug-in architecture under development, you can use it in other languages, including Swift.

Swift’s Prettier plug-in is important, but it crashes when it encounters an unregulated syntax marker (such as EnumDecl 😩). However, as you’ll see below, it’s too powerful to ignore so far, so it’s worth putting it in this article’s formatting tool PK.

The installation

To use Prettier and its Swift plug-in, you involve Node. There are many ways to install it, but Yarn 😻 is our favorite.

$ brew install yarn
$ yarn global add prettier prettier/plugin-swift
Copy the code

use

The command-line utility prettier is now available in the environment variable $PATH, where you can pass in files or paths to run it.

$ prettier Example.swift
Copy the code

Sample output

Here’s the output from running the latest version of the Swift plug-in using Prettier in our previous example:

/ / prettier version 1.16.4
// prettier/plugin-swift version 0.0.0 (bdf8726)
struct ShippingAddress: Codable {
    var recipient: String
    var streetAddress: String
    var locality: String
    var region: String
    var postalCode: String
    var country: String

    init(
        recipient: String,
        streetAddress: String,
        locality: String,
        region: String,
        postalCode: String,
        country: String
    ) {
        self.recipient = recipient
        self.streetAddress = streetAddress
        self.locality = locality
        self.region = region;
        self.postalCode = postalCode
        guard country.count= =2, country == country.uppercased() else {
            fatalError("invalid country code")}self.country = country
    }
}

let applePark = ShippingAddress(
    recipient: "Apple, Inc.",
    streetAddress: "1 Apple Park Way",
    locality: "Cupertino",
    region: "CA",
    postalCode: "95014",
    country: "US"
)
Copy the code

Prettier calls herself “a self-righteous code formatter.” In fact, there aren’t many ways to configure it, just two options: “Regular code” or “prettier code.”

Now, you might face a lot of vertical alignment, but you have to admit that this code looks really good. All statements are evenly spaced, indented, and aligned, and it’s hard to believe this is automatic.

Of course, our earlier warnings still apply: this is still under development, not suitable for production, and it has some performance issues.

performance

In the end, Prettier is an order of magnitude or two slower than the other tools discussed in this article.

$ time prettier Example.swift
    1.14 real         0.56 user         0.38 sys
Copy the code

It’s not clear whether this is the result of language barriers or poor optimization, but Prettier was slow enough to cause big problems.

For now, we recommend using Prettier only for one-time formatting tasks, such as coding articles and books.

swift-format

Knowing the current state of the art for Swift formatters, we now have a reasonable standard to evaluate the Swift-format tool proposed by Tony Allevato and Dave Abrahams.

The installation

Swift-format code is currently hosted on the Format branch of Swift in Google Fork. You can download it and compile it from source by running the following command:

$ git clone https://github.com/google/swift.git swift-format
$ cd swift-format
$ git submodule update --init
$ swift
Copy the code

For your convenience, we’ve provided a homemade formula from fork in our own Google repository that you can install with the following command:

$ brew install nshipster/formulae/swift-format
Copy the code

use

Run the swift-format command to pass in the swift file or directory to format.

$ swift-format Example.swift
Copy the code

The swift-format command also takes the –configuration option, which requires passing in a JSON file. Currently, the easiest way to customize swift-format behavior is to dump the default configuration into a file.

$ swift-format -m dump-configuration .swift-format.json
Copy the code

Create the following JSON file and pass it in when you run the above command:

{
  "blankLineBetweenMembers": {
    "ignoreSingleLineProperties": true
  },
  "indentation": {
    "spaces": 2
  },
  "lineLength": 100."maximumBlankLines": 1."respectsExistingLineBreaks": true."tabWidth": 8."version": 1
}
Copy the code

After configuration, you should use:

$ swift-format Example.swift --configuration .swift-format.json
Copy the code

Sample output

Using its default configuration, here is the output after swift-format formatting:

/ / swift - the format version 0.0.1
struct ShippingAddress: Codable {
  var recipient: String
  var streetAddress: String
  var locality: String
  var region   :String;
  var postalCode: String
  var country: String

  init(
    recipient: String, streetAddress: String,
    locality: String, region: String, postalCode: String, country: String
  )
  {
    self.recipient = recipient
    self.streetAddress = streetAddress
    self.locality = locality
    self.region = region
    self.postalCode = postalCode
    guard country.count= =2, country == country.uppercased() else {
      fatalError("invalid country code")}self.country = country
  }
}

let applePark = ShippingAddress(
  recipient: "Apple, Inc.", streetAddress: "1 Apple Park Way", locality: "Cupertino", region: "CA",
  postalCode: "95014", country: "US")
Copy the code

With the release of version 0.0.1, this looks promising! We can do this without the original semicolon, and we don’t care too much about the colon position of the region attribute, but overall, this is fine, and it’s the official code formatter you want.

performance

In terms of performance, swift-format is currently in the middle, neither fast nor slow.

$ time swift-format Example.swift
        0.51 real         0.20 user         0.27 sys
Copy the code

Based on our limited preliminary investigation, swift-format appears to provide a reasonable set of formatting conventions. Hopefully it will create more vivid examples to help us understand the formatting rules in the future.

In any case, it will be interesting to see how the proposals develop and the discussions surrounding these issues.


NSMutableHipster

If you have other questions, please send us Issues and pull requests.

This article uses Swift 5.0. You can find the status information for all articles on the status page.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.