Skip to content

Commit

Permalink
WIP Initial commit
Browse files Browse the repository at this point in the history
This creates a Swift package with an example app, linting and formatting
commands, CI pipeline, README, and contributing guidelines.

In the absence of guidance or data about which minimum Swift version to
use, I decided to use the latest release at time of writing (5.10). We
can revisit this later if need be.

The SwiftLint rules are taken from our Asset Tracking repo at commit
b39e5187. They seemed like a good starting point (I had previously done
some thinking about these rules in [1]), and we can tweak them as
needed. I’ve enabled the private_swiftui_state rule (seems useful) and
also inverted its default trailing comma rule (for compatibility with
SwiftFormat).

Upon realising that SwiftLint and SwiftFormat have clashing opinions on
trailing commas, I worried that there might turn out to be other clashes
between the two. So I then hoped that we could get away with just using
SwiftLint, which does have some style rules. But it doesn’t enforce some
basic things like indentation. I hope that there aren’t further clashes
between the two tools, but we won’t find out until we write more code.

We use Mint to run SwiftLint and SwiftFormat. This allows us to lock
their versions, which we can’t do with Homebrew. It’s unfortunate that
for some reason SwiftLint takes an eternity to build using Mint, but
it’s a one-time thing per developer. I originally ran these tools as
Swift package plugins, but SwiftLint’s plugin was crashing when run in
Xcode 16 beta, hence the switch to Mint. Maybe we can switch back
sometime.

The build scripts used by CI, as well as the linting scripts, are
implemented as a Swift command-line tool. Hopefully we’ll find this
easier to maintain and modify than a set of Bash scripts.

[1] ably/ably-asset-tracking-swift#629
  • Loading branch information
lawrence-forooghian committed Aug 7, 2024
0 parents commit 3609e0a
Show file tree
Hide file tree
Showing 36 changed files with 1,812 additions and 0 deletions.
114 changes: 114 additions & 0 deletions .github/workflows/check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: Check

on:
workflow_dispatch:
pull_request:
push:
branches:
- main
jobs:
lint:
runs-on: macos-latest

# From actions/cache documentation linked to below
env:
MINT_PATH: .mint/lib
MINT_LINK_PATH: .mint/bin

steps:
- uses: actions/checkout@v4

# We use caching for Mint because at the time of writing SwiftLint took about 5 minutes to build in CI, which is unacceptably slow.
# https://github.com/actions/cache/blob/40c3b67b2955d93d83b27ed164edd0756bc24049/examples.md#swift---mint
- uses: actions/cache@v4
with:
path: .mint
key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }}
restore-keys: |
${{ runner.os }}-mint-
- run: npm ci
- run: brew install mint
- run: mint bootstrap

- run: swift run BuildTool lint

generate-matrices:
runs-on: macos-latest
outputs:
matrix: ${{ steps.generation-step.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- id: generation-step
run: swift run BuildTool generate-matrices >> $GITHUB_OUTPUT

check-spm:
name: SPM (Xcode ${{ matrix.tooling.xcodeVersion }}, Swift ${{ matrix.tooling.swiftVersion }})
runs-on: macos-latest
needs: generate-matrices
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.generate-matrices.outputs.matrix).withoutPlatform }}

steps:
- uses: actions/checkout@v4
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ matrix.tooling.xcodeVersion }}

# https://forums.swift.org/t/warnings-as-errors-for-libraries-frameworks/58393/2
- run: swift build -Xswiftc -warnings-as-errors -Xswiftc -swift-version -Xswiftc ${{ matrix.tooling.swiftVersion }}
- run: swift test -Xswiftc -warnings-as-errors -Xswiftc -swift-version -Xswiftc ${{ matrix.tooling.swiftVersion }}

check-xcode:
name: Xcode, ${{matrix.platform}} (Xcode ${{ matrix.tooling.xcodeVersion }}, Swift ${{ matrix.tooling.swiftVersion }})
runs-on: macos-latest
needs: generate-matrices

strategy:
fail-fast: false
matrix: ${{ fromJson(needs.generate-matrices.outputs.matrix).withPlatform }}

steps:
- uses: actions/checkout@v4
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ matrix.tooling.xcodeVersion }}

- name: Build and run tests
run: swift run BuildTool build-and-test-library --platform ${{ matrix.platform }} --swift-version ${{ matrix.tooling.swiftVersion }}

check-example-app:
name: Example app, ${{matrix.platform}} (Xcode ${{ matrix.tooling.xcodeVersion }}, Swift ${{ matrix.tooling.swiftVersion }})
runs-on: macos-latest
needs: generate-matrices

strategy:
fail-fast: false
matrix: ${{ fromJson(needs.generate-matrices.outputs.matrix).withPlatform }}

steps:
- uses: actions/checkout@v4
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ matrix.tooling.xcodeVersion }}

- name: Build example app
run: swift run BuildTool build-example-app --platform ${{ matrix.platform }} --swift-version ${{ matrix.tooling.swiftVersion }}

# We use this job as a marker that all of the required checks have completed.
# This allows us to configure a single required status check in our branch
# protection rules instead of having to type loads of different check names
# into the branch protection web UI (and keep this list up to date as we
# tweak the matrices).
all-checks-completed:
runs-on: ubuntu-latest
needs:
- lint
- check-spm
- check-xcode
- check-example-app

steps:
- name: No-op
run: "true"
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Start of .gitignore created by Swift Package Manager
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
# End of .gitignore created by Swift Package Manager

/node_modules
/.mint
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Don’t try and format the asset catalogue JSON files, which are managed by Xcode
*.xcassets/
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions .swift-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5.10
102 changes: 102 additions & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
excluded:
- .build

strict: true

disabled_rules:
# All of the default rules of type "metrics". We have no reason to believe that the arbitrary defaults picked by SwiftLint are helpful.
- cyclomatic_complexity
- file_length
- function_body_length
- function_parameter_count
- large_tuple
- line_length
- nesting
- type_body_length

# Rules of type "lint" that we’ve decided we don’t want:
- todo # We frequently use TODOs accompanied by a GitHub issue reference

opt_in_rules:
# All of the opt-in rules of type "performance":
- contains_over_filter_count
- contains_over_filter_is_empty
- contains_over_first_not_nil
- contains_over_range_nil_comparison
- empty_collection_literal
- empty_count
- empty_string
- first_where
- flatmap_over_map_reduce
- last_where
- reduce_into
- sorted_first_last

# Opt-in rules of type "style" that we’ve decided we want:
- attributes
- closure_end_indentation
- closure_spacing
- collection_alignment
- comma_inheritance
- conditional_returns_on_newline
- file_header
- implicit_return
- literal_expression_end_indentation
- modifier_order
- multiline_arguments
- multiline_arguments_brackets
- multiline_function_chains
- multiline_literal_brackets
- multiline_parameters
- multiline_parameters_brackets
- operator_usage_whitespace
- prefer_self_type_over_type_of_self
- self_binding
- single_test_class
- sorted_imports
- switch_case_on_newline
- trailing_closure
- trailing_newline
- unneeded_parentheses_in_closure_argument
- vertical_parameter_alignment_on_call
- vertical_whitespace_closing_braces
- vertical_whitespace_opening_braces

# Opt-in rules of type "idiomatic" that we’ve decided we want:
- anonymous_argument_in_multiline_closure
- convenience_type
- fallthrough
- fatal_error_message
- pattern_matching_keywords
- redundant_type_annotation
- shorthand_optional_binding
- static_operator
- toggle_bool
- xct_specific_matcher

# Opt-in rules of type "lint" that we’ve decided we want:
- array_init
- empty_xctest_method
- missing_docs
- override_in_extension
- yoda_condition
- private_swiftui_state

file_header:
# Comments, except for the required and standard ones at the top of a Package.swift file
forbidden_pattern: //(?! (swift-tools-version:|The swift-tools-version declares the minimum version of Swift required to build this package\.))

identifier_name:
&no_length_checks # We disable the length checks, for the same reason we disable the rules of type "metrics".
min_length:
warning: 1
max_length:
warning: 10000

type_name: *no_length_checks

generic_type_name: *no_length_checks

# For compatibility with SwiftFormat
trailing_comma:
mandatory_comma: true
42 changes: 42 additions & 0 deletions .swiftpm/configuration/Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"originHash" : "9d852a8f936d58dcf5f543508d205baeff66d9e230197f5987ee4f89aa7b1bf1",
"pins" : [
{
"identity" : "ably-cocoa",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ably/ably-cocoa",
"state" : {
"revision" : "7f639c609e50053abd4590f34333f9472645558a",
"version" : "1.2.33"
}
},
{
"identity" : "delta-codec-cocoa",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ably/delta-codec-cocoa",
"state" : {
"revision" : "3ee62ea40a63996b55818d44b3f0e56d8753be88",
"version" : "1.3.3"
}
},
{
"identity" : "msgpack-objective-c",
"kind" : "remoteSourceControl",
"location" : "https://github.com/rvi/msgpack-objective-C",
"state" : {
"revision" : "3e36b48e04ecd756cb927bd5f5b9bf6d45e475f9",
"version" : "0.4.0"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "41982a3656a71c768319979febd796c6fd111d5c",
"version" : "1.5.0"
}
}
],
"version" : 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
<true/>
</dict>
</plist>
79 changes: 79 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/AblyChat.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1540"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AblyChat"
BuildableName = "AblyChat"
BlueprintName = "AblyChat"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AblyChatTests"
BuildableName = "AblyChatTests"
BlueprintName = "AblyChatTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AblyChat"
BuildableName = "AblyChat"
BlueprintName = "AblyChat"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
10 changes: 10 additions & 0 deletions AblyChat.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions AblyChat.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
Loading

0 comments on commit 3609e0a

Please sign in to comment.