How to Lint and Format KMM iOS Apps with Danger, SwiftLint & SwiftFormat

How to Lint and Format KMM iOS Apps with Danger, SwiftLint & SwiftFormat

A starter guide for adding lint and code format for a Kotlin Multiplatform Mobile iOS application, with Danger.
Pisit Wetchayanwiwat
Pisit Wetchayanwiwat
March 18, 2025
iOS Mobile

Table of Contents

Introduction

We have added linting for the Kotlin Android application, but that is only half of the KMM architecture. The other half, Native Swift (Swift), is missing out on all the benefits of Danger.

In this article, we will explore static code analysis on iOS, covering local linting, integrating linting into a Continuous Integration (CI) flow using GitHub Actions, and finally, generating and publishing a summary report for each Pull Request.

Why lint

💡 TL;DR: Lint is an efficient way to ensure code quality.

Read more about why linting in the first part “Linting KMM Android Application with Danger”.

Lint with SwiftLint

SwiftLint is a Swift linter tool for Swift that enforces iOS coding standards automatically.

There are multiple ways to install SwiftLint. The recommended way is via CocoaPods because:

  • KMM project uses CocoaPods to connect the Native application to the Kotlin modules.
  • Keep SwiftLint dependent on the project.

Add the following pod to the Podfile, then run pod install in the iosApp directory.

target 'iosApp' do
	# Other pods 
	# ...
  pod 'SwiftLint'
  # ...
end

SwiftLint is run via Pods/SwiftLint/SwiftLint inside the iosApp folder or iosApp/Pods/SwiftLint/SwiftLint from the root folder.

Configure SwiftLint Rules

SwiftLint allows rule configuration via .SwiftLint.yml. Here is an example configuration.

excluded:
  - Pods
  - Derived
  - DerivedData

💡 SwiftLint can automatically display linting warnings and errors in Xcode during code compilation, helping developers fix issues during development. Setup instructions are available on the project’s GitHub page.

Now that we’ve covered SwiftLint, let’s explore how Danger can automate our linting workflow.

SwiftLint with Danger

We will create a new Gemfile and Dangerfile inside the iosApp directory, separated from the KMM modules.

Add the danger and CocoaPods gems to the iosApp/Gemfile.

# Gemfile
source "https://rubygems.org"

gem "CocoaPods"
gem "danger"
gem "danger-SwiftLint"

Add SwiftLint plugin to iosApp/Dangerfile. Danger will execute SwiftLint from the plugin.

# Dangerfile

# SwiftLint
SwiftLint.binary_path = 'iosApp/Pods/SwiftLint/SwiftLint'
SwiftLint.config_file = 'iosApp/.SwiftLint.yml'
SwiftLint.max_num_violations = 20
SwiftLint.lint_files(
  inline_mode: true,
  fail_on_error: true, # Stop CI when fail 
  additional_SwiftLint_args: '--strict'
)

Create a CI/CD workflow that performs actions within the iosApp subdirectory.

name: iOS Review pull request

on:
  pull_request

defaults:
  run:
    working-directory: iosApp

jobs:
  review_pull_request:
    name: Review pull request
    runs-on: macos-latest
    timeout-minutes: 30
    steps:
      - name: Set up JAVA 11
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: '11'

      - name: Checkout source code
        uses: actions/checkout@v4
        
      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.4'
          bundler-cache: true
          working-directory: 'iosApp'
          
      - name: Install gems
        run: |
          bundle install

Danger will now run SwiftLint and comment on the files in a pull request.

Finally, add Danger execution to the end of CI/CD workflow such as this GitHub Actions example.

...
- name: Run iOS Danger
  uses: MeilCli/danger-action@v5
  with:
    plugins_file: 'iosApp/Gemfile'
    install_path: 'iosApp/vendor/bundle'
    danger_file: 'iosApp/Dangerfile'
    danger_id: 'danger-pr'
  env:
    DANGER_GITHUB_API_TOKEN: $
SwiftLint on Github
SwiftLint on Github

Linting with SwiftFormat

SwiftFormat is a code reformatter for Swift. It supports most of SwiftLint’s format and a few extra. SwiftFormat automatically formats your Swift code before pushing, ensuring it adheres to project conventions.

With Danger, SwiftFormat can be used for linting Swift code on the CI/CD, to check for some conventions that SwiftLint is missing.

Adding SwiftFormat

Add SwiftFormat as a CocoaPods dependency to the Podfile

target 'iosApp' do
	# Other pods 
	# ...
	pod 'SwiftFormat/CLI'
  # ...
end

💡 The SwiftFormat Github page shows multiple ways to install SwiftFormat. Using CocoaPods is the preferred way because Danger will require the path for SwiftFormat later on.

SwiftFormat can now be executed by running iosApp/Pods/SwiftFormat/CommandLineTool/swiftformat.

SwiftFormat can be configured with iosApp/.swiftformat file. Here is an example of .swiftformat file.

# file options
--exclude Pods, Generated, **/*.generated.swift

# rules
--disable fileHeader
--disable initCoderUnavailable

SwiftFormat with Danger

Add the SwiftFormat Danger plugin to the iosApp/Gemfile.

# Gemfile
source "https://rubygems.org"

gem "CocoaPods"
gem "danger"
gem 'danger-swiftformat'

The last step is to add SwiftFormat to iosApp/Dangerfile.

# SwiftFormat
swiftformat.binary_path = 'iosApp/Pods/SwiftFormat/CommandLineTool/swiftformat'
swiftformat.exclude = %w(Pods/**  **/*generated.swift)
swiftformat.check_format

Make sure to bundle exec danger or use the Danger action in the CI/CD workflow.

SwiftFormat by Danger on a pull request
SwiftFormat by Danger on a pull request

Conclusion

Linting and formatting are essential for maintaining a clean, high-quality codebase in KMM projects. By integrating SwiftLintSwiftFormat, and Danger, you can automate these checks, streamline code reviews, and prevent technical debt from creeping into your iOS codebase.

To learn more about code coverage with Danger, make sure to read the upcoming KMM iOS Code Coverage blog post, or read about KMM Android lint and KMM Android code coverage.

References

If this is the kind of challenges you wanna tackle, Nimble is hiring awesome web and mobile developers to join our team in Bangkok, Thailand, Ho Chi Minh City, Vietnam, and Da Nang, Vietnam✌️

Join Us

Recommended Stories:

Accelerate your digital transformation.

Subscribe to our newsletter and get latest news and trends from Nimble