diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf26ba9..0d216be 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,7 @@ name: Build on: pull_request: - branches: - - main - - develop + branches: [ main ] jobs: build: diff --git a/.github/workflows/swiftformat.yml b/.github/workflows/swiftformat.yml index 1eba742..e865a0a 100644 --- a/.github/workflows/swiftformat.yml +++ b/.github/workflows/swiftformat.yml @@ -2,9 +2,7 @@ name: SwiftFormat Check on: pull_request: - branches: - - main - - develop + branches: [ main ] jobs: swiftformat: @@ -19,9 +17,4 @@ jobs: run: brew install swiftformat - name: Check code formatting - run: | - swiftformat --lint . - if [ $? -ne 0 ]; then - echo "❌ Code formatting issues detected. Please run 'swiftformat .' locally to fix." - exit 1 - fi + run: swiftformat --lint . diff --git a/.github/workflows/swiftlint.yml b/.github/workflows/swiftlint.yml new file mode 100644 index 0000000..ca493da --- /dev/null +++ b/.github/workflows/swiftlint.yml @@ -0,0 +1,20 @@ +name: SwiftLint Check + +on: + pull_request: + branches: [ main ] + +jobs: + swiftlint: + name: SwiftLint + runs-on: macos-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install SwiftLint + run: brew install swiftlint + + - name: Run SwiftLint + run: swiftlint lint --reporter github-actions-logging diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 437d9af..027a3d0 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -2,9 +2,7 @@ name: Unit Tests on: pull_request: - branches: - - main - - develop + branches: [ main ] jobs: test: diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..9d0af0e --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,64 @@ +# Minimal SwiftLint configuration +# SwiftFormat handles all formatting, so SwiftLint focuses on code quality and best practices + +disabled_rules: + - line_length + - opening_brace + - function_parameter_count + - nesting + - large_tuple + - type_name + - force_unwrapping + - blanket_disable_command + +opt_in_rules: + - array_init + - contains_over_first_not_nil + - convenience_type + - discouraged_assert + - discouraged_object_literal + - empty_count + - empty_string + - empty_xctest_method + - explicit_init + - fatal_error_message + - first_where + - force_cast + - force_try + - implicit_return + - joined_default_parameter + - last_where + - legacy_random + - lower_acl_than_parent + - multiline_function_chains + - multiline_parameters + - multiline_parameters_brackets + - no_fallthrough_only + - operator_usage_whitespace + - overridden_super_call + - prohibited_super_call + - redundant_nil_coalescing + - single_test_class + - sorted_first_last + - static_operator + - switch_case_alignment + - trailing_closure + - unavailable_function + - unneeded_parentheses_in_closure_argument + - unused_control_flow_label + - vertical_whitespace_closing_braces + - xct_specific_matcher + - xctfail_message + - yoda_condition + +multiline_parameters: + allows_single_line: false + +excluded: + - .build + - "**/.build" + - DerivedData + - Pods + - Carthage + - .git + - node_modules diff --git a/OMG/TritonEnvironment.swift b/OMG/TritonEnvironment.swift index 4fed848..01eeb5b 100644 --- a/OMG/TritonEnvironment.swift +++ b/OMG/TritonEnvironment.swift @@ -207,6 +207,7 @@ struct TritonEnvironment: TritonEnvironmentProtocol { ) } + // swiftlint:disable function_body_length init( authSessionServiceFactory: any AuthSessionServiceFactoryProtocol, sessionServiceFactory: any SessionServiceFactoryProtocol, @@ -233,11 +234,9 @@ struct TritonEnvironment: TritonEnvironmentProtocol { allocation: .static ) { container in let authSessionService = container.resolve() as any AuthSessionServiceProtocol - return networkClient.makeOMGAPIClient( - authTokenProvider: { - await authSessionService.accessToken - } - ) + return networkClient.makeOMGAPIClient { + await authSessionService.accessToken + } } container.register( @@ -375,4 +374,5 @@ struct TritonEnvironment: TritonEnvironmentProtocol { ) } } + // swiftlint:enable function_body_length } diff --git a/OMG/TritonScene.swift b/OMG/TritonScene.swift index ac9d6f7..d43ec2b 100644 --- a/OMG/TritonScene.swift +++ b/OMG/TritonScene.swift @@ -41,8 +41,8 @@ struct TritonScene: Scene { #endif .environment(makeAccountUpdateService()) .handlesExternalEvents( - preferring: Set(arrayLiteral: "viewer"), - allowing: Set(arrayLiteral: "*") + preferring: ["viewer"], + allowing: ["*"] ) .onAppear { environment diff --git a/Packages/AccountUpdate/Sources/AccountUpdateRepository/AccountUpdateRepository.swift b/Packages/AccountUpdate/Sources/AccountUpdateRepository/AccountUpdateRepository.swift index 524949f..586ddef 100644 --- a/Packages/AccountUpdate/Sources/AccountUpdateRepository/AccountUpdateRepository.swift +++ b/Packages/AccountUpdate/Sources/AccountUpdateRepository/AccountUpdateRepository.swift @@ -70,10 +70,8 @@ actor AccountUpdateRepository: AccountUpdateRepositoryProtocol { await fetchAccountInformation() } - for await isLoggedIn in authSessionService.observeLoginState() { - if isLoggedIn { - await fetchAccountInformation() - } + for await isLoggedIn in authSessionService.observeLoginState() where isLoggedIn { + await fetchAccountInformation() } } } diff --git a/Packages/Auth/Sources/Auth/Views/App/AuthAppViewModel.swift b/Packages/Auth/Sources/Auth/Views/App/AuthAppViewModel.swift index c8db94a..3b14fd7 100644 --- a/Packages/Auth/Sources/Auth/Views/App/AuthAppViewModel.swift +++ b/Packages/Auth/Sources/Auth/Views/App/AuthAppViewModel.swift @@ -25,10 +25,8 @@ final class AuthAppViewModel { let currentState = await authSessionService.isLoggedIn isLoggedIn = currentState - for await loginState in authSessionService.observeLoginState() { - if isLoggedIn != loginState { - isLoggedIn = loginState - } + for await loginState in authSessionService.observeLoginState() where isLoggedIn != loginState { + isLoggedIn = loginState } } } diff --git a/Packages/AuthSession/Tests/AuthSessionServiceTests/AuthSessionServiceTests.swift b/Packages/AuthSession/Tests/AuthSessionServiceTests/AuthSessionServiceTests.swift index c841d1d..3e5d9ee 100644 --- a/Packages/AuthSession/Tests/AuthSessionServiceTests/AuthSessionServiceTests.swift +++ b/Packages/AuthSession/Tests/AuthSessionServiceTests/AuthSessionServiceTests.swift @@ -81,7 +81,7 @@ final class AuthSessionServiceTests: XCTestCase { ) } - func testObserveLoginStateYieldsCurrentState() async { + func testObserveLoginStateYieldsCurrentState() async throws { // Given let initialState = await service.isLoggedIn @@ -97,9 +97,8 @@ final class AuthSessionServiceTests: XCTestCase { var iterator = stream.makeAsyncIterator() let firstValue = await iterator.next() - XCTAssertEqual( - firstValue, - false, + XCTAssertFalse( + try XCTUnwrap(firstValue), "It should yielded the first value correctly" ) } diff --git a/Packages/DesignSystem/Sources/DesignSystem/Toolbar Items/SelectionToolbarItem.swift b/Packages/DesignSystem/Sources/DesignSystem/Toolbar Items/SelectionToolbarItem.swift index 0a3ab79..d317a75 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Toolbar Items/SelectionToolbarItem.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Toolbar Items/SelectionToolbarItem.swift @@ -185,10 +185,10 @@ public struct SelectionToolbarItem: View { DropdownMenuView( options: options, selection: selection, - itemLabel: itemLabel, - label: { - AnyView(style.makeLabel(helpText: helpText ?? style.defaultHelpText)) - } - ) + itemLabel: itemLabel + ) { + let helpText = helpText ?? style.defaultHelpText + return AnyView(style.makeLabel(helpText: helpText)) + } } } diff --git a/Packages/DesignSystem/Sources/DesignSystem/Views/TagList/TagListView.swift b/Packages/DesignSystem/Sources/DesignSystem/Views/TagList/TagListView.swift index bc02f96..3032c95 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Views/TagList/TagListView.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Views/TagList/TagListView.swift @@ -78,14 +78,16 @@ public struct TagListView: View { #Preview("Regular Tags") { TagListView( tags: ["swift", "ios", "macos"], - helpText: { "Select \($0)" } - ) { _ in } + helpText: { "Select \($0)" }, + action: { _ in } + ) } #Preview("Remove Tags") { TagListView( tags: ["swift", "ios", "macos"], style: .remove, - helpText: { "Remove \($0)" } - ) { _ in } + helpText: { "Remove \($0)" }, + action: { _ in } + ) } diff --git a/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/ArrayContainsTests.swift b/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/ArrayContainsTests.swift index 1f5ab7d..1b6caf4 100644 --- a/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/ArrayContainsTests.swift +++ b/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/ArrayContainsTests.swift @@ -1,6 +1,8 @@ import Testing @testable import FoundationExtensions +// swiftlint:disable file_length type_body_length + @Suite("ArrayContains Tests") struct ArrayContainsTests { @@ -535,3 +537,5 @@ struct ArrayContainsTests { ) } } + +// swiftlint:enable file_length type_body_length diff --git a/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/StringSlugTests.swift b/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/StringSlugTests.swift index 78c2f22..8a1e01d 100644 --- a/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/StringSlugTests.swift +++ b/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/StringSlugTests.swift @@ -90,7 +90,7 @@ struct StringSlugTests { // Then #expect( - result == "", + result.isEmpty, "It should handle empty string" ) } @@ -105,7 +105,7 @@ struct StringSlugTests { // Then #expect( - result == "", + result.isEmpty, "It should handle string with only whitespace" ) } diff --git a/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/StringWeblogTests.swift b/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/StringWeblogTests.swift index 15c7dc4..222b4c4 100644 --- a/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/StringWeblogTests.swift +++ b/Packages/FoundationExtensions/Tests/FoundationExtensionsTests/StringWeblogTests.swift @@ -2,6 +2,8 @@ import Foundation import Testing @testable import FoundationExtensions +// swiftlint:disable file_length type_body_length + @Suite("StringWeblog Tests") struct StringWeblogTests { @@ -496,3 +498,5 @@ struct StringWeblogTests { ) } } + +// swiftlint:enable file_length type_body_length diff --git a/Packages/Now/Sources/Now/Environment/NowEnvironment.swift b/Packages/Now/Sources/Now/Environment/NowEnvironment.swift index 2d007b9..ee1408e 100644 --- a/Packages/Now/Sources/Now/Environment/NowEnvironment.swift +++ b/Packages/Now/Sources/Now/Environment/NowEnvironment.swift @@ -41,6 +41,7 @@ struct NowEnvironment { ) } + // swiftlint:disable function_body_length init( networkServiceFactory: NowNetworkServiceFactoryProtocol, persistenceServiceFactory: NowPersistenceServiceFactoryProtocol, @@ -113,4 +114,5 @@ struct NowEnvironment { ) } } + // swiftlint:enable function_body_length } diff --git a/Packages/Now/Sources/Now/Fixtures/NowMother.swift b/Packages/Now/Sources/Now/Fixtures/NowMother.swift index 98bc8f8..6a6989c 100644 --- a/Packages/Now/Sources/Now/Fixtures/NowMother.swift +++ b/Packages/Now/Sources/Now/Fixtures/NowMother.swift @@ -12,12 +12,12 @@ count: Int, in container: ModelContainer ) { - for i in 0.. [PictureResponse] { [] } - func deletePicture(address: String, pictureID: String) async throws {} + func fetchPictures( + for address: String + ) async throws -> [PictureResponse] { [] } + + func deletePicture( + address: String, + pictureID: String + ) async throws {} + func updatePicture( address: String, pictureID: String, @@ -21,6 +28,7 @@ alt: String, tags: [String] ) async throws {} + func uploadPicture( address: String, data: Data, diff --git a/Packages/Pics/Sources/Pics/Fixtures/PicsRepositoryMother.swift b/Packages/Pics/Sources/Pics/Fixtures/PicsRepositoryMother.swift index b188376..da488d8 100644 --- a/Packages/Pics/Sources/Pics/Fixtures/PicsRepositoryMother.swift +++ b/Packages/Pics/Sources/Pics/Fixtures/PicsRepositoryMother.swift @@ -39,7 +39,12 @@ // MARK: - Public func fetchPictures() async throws {} - func deletePicture(address: String, pictureID: String) async throws {} + + func deletePicture( + address: String, + pictureID: String + ) async throws {} + func updatePicture( address: String, pictureID: String, @@ -47,6 +52,7 @@ alt: String, tags: [String] ) async throws {} + func uploadPicture( address: String, data: Data, diff --git a/Packages/Pics/Sources/Pics/Fixtures/SomePictureMother.swift b/Packages/Pics/Sources/Pics/Fixtures/SomePictureMother.swift index 7e2bd43..feb528a 100644 --- a/Packages/Pics/Sources/Pics/Fixtures/SomePictureMother.swift +++ b/Packages/Pics/Sources/Pics/Fixtures/SomePictureMother.swift @@ -13,9 +13,9 @@ count: Int = 3, in container: ModelContainer ) { - for i in 0.. [EntryResponse] { - (0.. EntryResponse { + func fetchWeblogEntry( + for address: String, + entryID: String + ) async throws -> EntryResponse { EntryResponseMother.makeEntryResponse() } - func fetchWeblogEntries(for address: String) async throws -> [EntryResponse] { + func fetchWeblogEntries( + for address: String + ) async throws -> [EntryResponse] { EntryResponseMother.makeEntryResponses(count: 2) } @@ -41,7 +46,10 @@ EntryResponseMother.makeEntryResponse() } - func deleteWeblogEntry(address: String, entryID: String) async throws {} + func deleteWeblogEntry( + address: String, + entryID: String + ) async throws {} } // MARK: - Public diff --git a/Packages/Weblog/Sources/Weblog/Fixtures/WeblogRepositoryMother.swift b/Packages/Weblog/Sources/Weblog/Fixtures/WeblogRepositoryMother.swift index e70e7dd..a212e3a 100644 --- a/Packages/Weblog/Sources/Weblog/Fixtures/WeblogRepositoryMother.swift +++ b/Packages/Weblog/Sources/Weblog/Fixtures/WeblogRepositoryMother.swift @@ -39,7 +39,11 @@ // MARK: - Public func fetchEntries() async throws {} - func deleteEntry(address: String, entryID: String) async throws {} + + func deleteEntry( + address: String, + entryID: String + ) async throws {} func createOrUpdateEntry( address: String, diff --git a/Packages/Weblog/Sources/Weblog/Views/Editor/EditorView.swift b/Packages/Weblog/Sources/Weblog/Views/Editor/EditorView.swift index c188173..136a6d3 100644 --- a/Packages/Weblog/Sources/Weblog/Views/Editor/EditorView.swift +++ b/Packages/Weblog/Sources/Weblog/Views/Editor/EditorView.swift @@ -53,6 +53,7 @@ struct EditorView: View { } @ViewBuilder + // swiftlint:disable:next function_body_length private func makeSidebarView() -> some View { Grid(alignment: .leading, horizontalSpacing: 16, verticalSpacing: 16) { GridRow(alignment: .firstTextBaseline) { @@ -146,12 +147,13 @@ struct EditorView: View { if !viewModel.suggestedTags.isEmpty { TagListView( tags: viewModel.suggestedTags, - helpText: { "Add existing tag '\($0)'" } - ) { tag in - withAnimation { - viewModel.addTag(tag) + helpText: { "Add existing tag '\($0)'" }, + action: { tag in + withAnimation { + viewModel.addTag(tag) + } } - } + ) } } @@ -170,12 +172,13 @@ struct EditorView: View { TagListView( tags: viewModel.tags, style: .remove, - helpText: { "Remove tag '\($0)'" } - ) { tag in - withAnimation { - viewModel.removeTag(tag) + helpText: { "Remove tag '\($0)'" }, + action: { tag in + withAnimation { + viewModel.removeTag(tag) + } } - } + ) } } diff --git a/Packages/Weblog/Sources/Weblog/Views/Weblog Entry List/WeblogEntriesListView.swift b/Packages/Weblog/Sources/Weblog/Views/Weblog Entry List/WeblogEntriesListView.swift index f6e3488..cfd59d0 100644 --- a/Packages/Weblog/Sources/Weblog/Views/Weblog Entry List/WeblogEntriesListView.swift +++ b/Packages/Weblog/Sources/Weblog/Views/Weblog Entry List/WeblogEntriesListView.swift @@ -21,7 +21,7 @@ struct WeblogEntriesListView: View { _entries = .init(viewModel.fetchDescriptor()) } - // MARK; - Public + // MARK: - Public var body: some View { Group { diff --git a/Packages/Webpage/Sources/Webpage/Environment/WebpageEnvironment.swift b/Packages/Webpage/Sources/Webpage/Environment/WebpageEnvironment.swift index ddf65fd..34bb75a 100644 --- a/Packages/Webpage/Sources/Webpage/Environment/WebpageEnvironment.swift +++ b/Packages/Webpage/Sources/Webpage/Environment/WebpageEnvironment.swift @@ -40,6 +40,7 @@ struct WebpageEnvironment { ) } + // swiftlint:disable function_body_length init( networkServiceFactory: WebpageNetworkServiceFactoryProtocol, persistenceServiceFactory: WebpagePersistenceServiceFactoryProtocol, @@ -112,4 +113,5 @@ struct WebpageEnvironment { ) } } + // swiftlint:enable function_body_length } diff --git a/Packages/Webpage/Sources/Webpage/Fixtures/WebpageMother.swift b/Packages/Webpage/Sources/Webpage/Fixtures/WebpageMother.swift index 7a1c2d1..959ad85 100644 --- a/Packages/Webpage/Sources/Webpage/Fixtures/WebpageMother.swift +++ b/Packages/Webpage/Sources/Webpage/Fixtures/WebpageMother.swift @@ -12,11 +12,11 @@ count: Int, in container: ModelContainer ) { - for i in 0..