Skip to content

Commit 21c26bd

Browse files
author
Nick MacCarthy
committed
Fix pre-commit hooks: use local Homebrew tools + relax lint rules
- Switch from source-compiled hooks to local system tools (instant) - Install swiftlint/swiftformat via Homebrew (pre-compiled) - Relax SwiftLint rules to allow existing code patterns - Add .swiftformat config with Swift 5.9 version - Auto-format all Swift files for consistency - Remove --strict from swiftlint (warnings don't fail commits)
1 parent ee34bd8 commit 21c26bd

59 files changed

Lines changed: 1799 additions & 1784 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
build-and-test:
1616
name: Build and Test
1717
runs-on: macos-15
18-
18+
1919
steps:
2020
- name: Checkout
2121
uses: actions/checkout@v4
@@ -64,7 +64,7 @@ jobs:
6464
lint:
6565
name: Swift Lint
6666
runs-on: macos-15
67-
67+
6868
steps:
6969
- name: Checkout
7070
uses: actions/checkout@v4

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
# Development plans (internal)
55
plans/
6-
PLAN*.md
6+
PLAN*.md
77
PHASE*.md
88

99
# Xcode user data

.pre-commit-config.yaml

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Pre-commit hooks for Stay Tuned iOS project
2-
# Install: brew install pre-commit && pre-commit install
2+
#
3+
# Setup:
4+
# brew install pre-commit swiftlint swiftformat
5+
# pre-commit install
6+
#
37
# Run manually: pre-commit run --all-files
48

59
repos:
@@ -18,19 +22,19 @@ repos:
1822
- id: mixed-line-ending
1923
args: ['--fix=lf']
2024

21-
# SwiftLint for Swift code linting
22-
- repo: https://github.com/realm/SwiftLint
23-
rev: 0.54.0
25+
# Local hooks using Homebrew-installed tools (fast!)
26+
- repo: local
2427
hooks:
2528
- id: swiftlint
26-
args: ['--strict', '--config', '.swiftlint.yml']
27-
# Only run on Swift files in the main project (not generated files)
28-
files: 'Stay Tuned/.*\.swift$'
29+
name: SwiftLint
30+
entry: swiftlint lint --config .swiftlint.yml
31+
language: system
32+
files: '\.swift$'
33+
exclude: 'Stay TunedTests|Stay TunedUITests'
2934

30-
# SwiftFormat for consistent code formatting
31-
- repo: https://github.com/nicklockwood/SwiftFormat
32-
rev: 0.53.0
33-
hooks:
3435
- id: swiftformat
35-
files: 'Stay Tuned/.*\.swift$'
36-
36+
name: SwiftFormat
37+
entry: swiftformat --config .swiftformat
38+
language: system
39+
files: '\.swift$'
40+
exclude: 'Stay TunedTests|Stay TunedUITests'

.swiftformat

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# SwiftFormat configuration for Stay Tuned
2+
# Documentation: https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md
3+
4+
# Swift version
5+
--swiftversion 5.9
6+
7+
# File options
8+
--exclude Stay Tuned/Stay Tuned.xcodeproj,Stay TunedTests,Stay TunedUITests,.build
9+
10+
# Format options
11+
--indent 4
12+
--indentcase false
13+
--trimwhitespace always
14+
--voidtype void
15+
--semicolons inline
16+
--wrapcollections before-first
17+
--wraparguments before-first
18+
--wrapparameters before-first
19+
--closingparen balanced
20+
--funcattributes prev-line
21+
--typeattributes prev-line
22+
--varattributes prev-line
23+
--marktypes never
24+
--markextensions never
25+
--extensionacl on-declarations
26+
27+
# SwiftUI-friendly options
28+
--self init-only
29+
--importgrouping testable-last
30+
--commas always
31+
32+
# Disabled rules (too aggressive for existing codebase)
33+
--disable redundantSelf
34+
--disable wrapMultilineStatementBraces
35+
--disable blankLinesAtStartOfScope
36+
--disable blankLinesAtEndOfScope

.swiftlint.yml

Lines changed: 13 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -11,85 +11,35 @@ excluded:
1111
- Stay Tuned/Stay TunedUITests
1212
- "**/.build"
1313

14-
# Enabled rules beyond defaults
15-
opt_in_rules:
16-
- closure_spacing
17-
- contains_over_filter_count
18-
- contains_over_filter_is_empty
19-
- contains_over_first_not_nil
20-
- empty_collection_literal
21-
- empty_count
22-
- empty_string
23-
- first_where
24-
- force_unwrapping
25-
- implicit_return
26-
- last_where
27-
- modifier_order
28-
- overridden_super_call
29-
- private_action
30-
- private_outlet
31-
- sorted_first_last
32-
- toggle_bool
33-
- unused_declaration
34-
- vertical_whitespace_closing_braces
35-
36-
# Disabled rules
14+
# Disabled rules - either too noisy or not applicable
3715
disabled_rules:
3816
- todo # Allow TODO comments during development
3917
- line_length # Can be noisy in SwiftUI with long modifiers
4018
- type_body_length # Views can get long
4119
- file_length # Some files are legitimately long
4220
- function_body_length # Some audio processing functions need to be long
4321
- cyclomatic_complexity # Audio algorithms can be complex
22+
- trailing_comma # Would require too many changes to existing code
23+
- force_unwrapping # Common in SwiftUI and audio code
24+
- implicit_return # Personal preference
25+
- for_where # Subjective style preference
26+
- identifier_name # Short names like t, r, g, b are common in graphics/audio
27+
- implicit_optional_initialization # Explicit nil is fine
28+
- function_parameter_count # Some functions need many parameters
29+
- void_function_in_ternary # Sometimes readable
30+
- vertical_whitespace_closing_braces # Minor style issue
31+
- redundant_string_enum_value # Explicit values are more readable
32+
- unused_closure_parameter # Sometimes intentional for readability
4433

4534
# Type name exceptions (Xcode auto-generates names with underscores)
4635
type_name:
4736
excluded:
4837
- Stay_TunedApp
4938

50-
# Rule configurations
51-
identifier_name:
52-
min_length:
53-
warning: 2
54-
error: 1
55-
max_length:
56-
warning: 50
57-
error: 60
58-
excluded:
59-
- id
60-
- i
61-
- j
62-
- x
63-
- y
64-
- db
65-
- hz
66-
- Hz
67-
68-
nesting:
69-
type_level:
70-
warning: 2
71-
function_level:
72-
warning: 3
73-
74-
trailing_comma:
75-
mandatory_comma: true
76-
77-
vertical_whitespace:
78-
max_empty_lines: 2
79-
80-
force_cast:
81-
severity: warning
82-
83-
force_try:
84-
severity: warning
85-
86-
force_unwrapping:
87-
severity: warning
88-
8939
# Custom rules
9040
custom_rules:
9141
no_print_statements:
9242
name: "No Print Statements"
9343
regex: '^\s*print\s*\('
9444
message: "Use os_log or remove debug print statements before committing"
95-
severity: warning
45+
severity: warning # Warning only, don't fail

LICENSE

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
SOFTWARE.
22-
23-

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,4 +203,3 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
203203
<p align="center">
204204
Made with 🎸 for musicians
205205
</p>
206-

Stay Tuned/Stay Tuned/Audio/AudioEngine.swift

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,21 @@ import Foundation
1313
final class AudioEngine: ObservableObject {
1414
private var audioEngine: AVAudioEngine?
1515
private var inputNode: AVAudioInputNode?
16-
17-
@Published private(set) var isRunning = false
18-
@Published private(set) var hasPermission = false
19-
16+
17+
@Published
18+
private(set) var isRunning = false
19+
@Published
20+
private(set) var hasPermission = false
21+
2022
var onBufferReceived: (([Float], Double) -> Void)?
21-
23+
2224
// Buffer size for audio capture
2325
private let bufferSize: AVAudioFrameCount = 1024
24-
26+
2527
init() {
2628
checkPermission()
2729
}
28-
30+
2931
func checkPermission() {
3032
let session = AVAudioSession.sharedInstance()
3133
switch session.recordPermission {
@@ -39,56 +41,56 @@ final class AudioEngine: ObservableObject {
3941
hasPermission = false
4042
}
4143
}
42-
44+
4345
func requestPermission() {
4446
AVAudioSession.sharedInstance().requestRecordPermission { [weak self] granted in
4547
DispatchQueue.main.async {
4648
self?.hasPermission = granted
4749
}
4850
}
4951
}
50-
52+
5153
func start() {
5254
guard hasPermission else {
5355
requestPermission()
5456
return
5557
}
56-
58+
5759
guard !isRunning else { return }
58-
60+
5961
do {
6062
let session = AVAudioSession.sharedInstance()
6163
try session.setCategory(.playAndRecord, mode: .measurement, options: [.defaultToSpeaker, .allowBluetooth])
6264
try session.setActive(true)
63-
65+
6466
guard session.isInputAvailable else { return }
65-
67+
6668
audioEngine = AVAudioEngine()
67-
guard let audioEngine = audioEngine else { return }
68-
69+
guard let audioEngine else { return }
70+
6971
inputNode = audioEngine.inputNode
70-
guard let inputNode = inputNode else { return }
71-
72+
guard let inputNode else { return }
73+
7274
inputNode.installTap(onBus: 0, bufferSize: bufferSize, format: nil) { [weak self] buffer, _ in
73-
guard let self = self else { return }
75+
guard let self else { return }
7476
guard let channelData = buffer.floatChannelData else { return }
75-
77+
7678
let channelDataValue = channelData.pointee
7779
let frameLength = Int(buffer.frameLength)
7880
let sampleRate = Double(buffer.format.sampleRate)
79-
81+
8082
// Copy samples
8183
var samples = [Float](repeating: 0, count: frameLength)
82-
for i in 0..<frameLength {
84+
for i in 0 ..< frameLength {
8385
samples[i] = channelDataValue[i]
8486
}
85-
87+
8688
// Call the callback
8789
self.onBufferReceived?(samples, sampleRate)
8890
}
89-
91+
9092
try audioEngine.start()
91-
93+
9294
DispatchQueue.main.async {
9395
self.isRunning = true
9496
}
@@ -99,18 +101,18 @@ final class AudioEngine: ObservableObject {
99101
}
100102
}
101103
}
102-
104+
103105
func stop() {
104106
inputNode?.removeTap(onBus: 0)
105107
audioEngine?.stop()
106108
audioEngine = nil
107109
inputNode = nil
108-
110+
109111
DispatchQueue.main.async {
110112
self.isRunning = false
111113
}
112114
}
113-
115+
114116
deinit {
115117
inputNode?.removeTap(onBus: 0)
116118
audioEngine?.stop()

0 commit comments

Comments
 (0)