Add SwiftLint

This commit is contained in:
Otavio Cordeiro 2026-01-13 12:42:30 -03:00 committed by Otávio
parent d3b596c26f
commit ee174ab836
66 changed files with 395 additions and 213 deletions

View file

@ -2,9 +2,7 @@ name: Build
on:
pull_request:
branches:
- main
- develop
branches: [ main ]
jobs:
build:

View file

@ -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 .

20
.github/workflows/swiftlint.yml vendored Normal file
View file

@ -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

View file

@ -2,9 +2,7 @@ name: Unit Tests
on:
pull_request:
branches:
- main
- develop
branches: [ main ]
jobs:
test:

64
.swiftlint.yml Normal file
View file

@ -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

View file

@ -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
}

View file

@ -41,8 +41,8 @@ struct TritonScene: Scene {
#endif
.environment(makeAccountUpdateService())
.handlesExternalEvents(
preferring: Set(arrayLiteral: "viewer"),
allowing: Set(arrayLiteral: "*")
preferring: ["viewer"],
allowing: ["*"]
)
.onAppear {
environment

View file

@ -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()
}
}
}

View file

@ -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
}
}
}

View file

@ -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"
)
}

View file

@ -185,10 +185,10 @@ public struct SelectionToolbarItem<Option: Hashable & CaseIterable>: 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))
}
}
}

View file

@ -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 }
)
}

View file

@ -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

View file

@ -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"
)
}

View file

@ -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

View file

@ -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
}

View file

@ -12,12 +12,12 @@
count: Int,
in container: ModelContainer
) {
for i in 0..<count {
for index in 0..<count {
let now = Now(
listed: true,
markdown: "Foobar \(i)",
markdown: "Foobar \(index)",
submitted: true,
timestamp: 123_123 * Double(i),
timestamp: 123_123 * Double(index),
address: "otaviocc"
)

View file

@ -17,8 +17,15 @@
}
}
func fetchNowPage(for address: String) async throws {}
func updateNowPage(address: String, content: String, listed: Bool) async throws {}
func fetchNowPage(
for address: String
) async throws {}
func updateNowPage(
address: String,
content: String,
listed: Bool
) async throws {}
}
// MARK: - Public

View file

@ -37,7 +37,12 @@
// MARK: - Public
func fetchNowPage() async throws {}
func updateNowPage(address: String, content: String, isListed: Bool) async throws {}
func updateNowPage(
address: String,
content: String,
isListed: Bool
) async throws {}
}
// MARK: - Public

View file

@ -3,5 +3,6 @@ import XCTest
final class NowTests: XCTestCase {
// swiftlint:disable:next empty_xctest_method
func testExample() throws {}
}

View file

@ -5,16 +5,4 @@ public struct CreateOrUpdatePasteRequest: Encodable, Sendable {
let title: String
let content: String
let listed: Bool
// MARK: - Lifecycle
init(
title: String,
content: String,
listed: Bool
) {
self.title = title
self.content = content
self.listed = listed
}
}

View file

@ -5,16 +5,4 @@ public struct CreatePURLRequest: Encodable, Sendable {
let address: String
let name: String
let url: String
// MARK: - Lifecycle
init(
address: String,
name: String,
url: String
) {
self.address = address
self.name = name
self.url = url
}
}

View file

@ -4,14 +4,4 @@ public struct UpdateNowPageRequest: Encodable, Sendable {
let content: String
let listed: Int
// MARK: - Lifecycle
init(
content: String,
listed: Int
) {
self.content = content
self.listed = listed
}
}

View file

@ -4,14 +4,4 @@ public struct UpdateWebpageRequest: Encodable, Sendable {
let content: String
let publish: Bool
// MARK: - Lifecycle
init(
content: String,
publish: Bool
) {
self.content = content
self.publish = publish
}
}

View file

@ -5,12 +5,4 @@ public struct UploadPictureRequest: Encodable, Sendable {
// MARK: - Properties
let pic: String
// MARK: - Lifecycle
init(
pic: String
) {
self.pic = pic
}
}

View file

@ -40,22 +40,22 @@ struct AuthRequestFactoryTests {
)
#expect(
queryItems.contains(where: { $0.name == "client_id" }),
queryItems.contains { $0.name == "client_id" },
"It should include client_id parameter"
)
#expect(
queryItems.contains(where: { $0.name == "scope" && $0.value == "everything" }),
queryItems.contains { $0.name == "scope" && $0.value == "everything" },
"It should include scope parameter with 'everything' value"
)
#expect(
queryItems.contains(where: { $0.name == "response_type" && $0.value == "code" }),
queryItems.contains { $0.name == "response_type" && $0.value == "code" },
"It should include response_type parameter with 'code' value"
)
#expect(
queryItems.contains(where: { $0.name == "redirect_uri" }),
queryItems.contains { $0.name == "redirect_uri" },
"It should include redirect_uri parameter"
)
}
@ -82,27 +82,27 @@ struct AuthRequestFactoryTests {
let queryItems = request.queryItems
#expect(
queryItems.contains(where: { $0.name == "client_id" }),
queryItems.contains { $0.name == "client_id" },
"It should include client_id query parameter"
)
#expect(
queryItems.contains(where: { $0.name == "client_secret" }),
queryItems.contains { $0.name == "client_secret" },
"It should include client_secret query parameter"
)
#expect(
queryItems.contains(where: { $0.name == "redirect_uri" }),
queryItems.contains { $0.name == "redirect_uri" },
"It should include redirect_uri query parameter"
)
#expect(
queryItems.contains(where: { $0.name == "code" && $0.value == authCode }),
queryItems.contains { $0.name == "code" && $0.value == authCode },
"It should include code query parameter with provided auth code"
)
#expect(
queryItems.contains(where: { $0.name == "scope" && $0.value == "everything" }),
queryItems.contains { $0.name == "scope" && $0.value == "everything" },
"It should include scope query parameter"
)
}

View file

@ -232,7 +232,7 @@ struct PicsRequestFactoryTests {
}
@Test("It should create picture edit request with empty tags array")
func makeEditPictureRequest_withOnlyWithEmptyTagsArray_createsRequest() {
func makeEditPictureRequest_withOnlyWithEmptyTagsArray_createsRequest() throws {
// Given
let address = "dave"
let pictureID = "pic-456"
@ -256,23 +256,26 @@ struct PicsRequestFactoryTests {
"It should use PUT method"
)
let body = try #require(request.body)
let bodyTags = try #require(body.tags)
#expect(
request.body?.tags == "",
bodyTags.isEmpty == true,
"It should include tags in request body"
)
#expect(
request.body?.caption == nil,
body.caption == nil,
"It should have nil caption when not provided"
)
#expect(
request.body?.alt == nil,
body.alt == nil,
"It should have nil alt text when not provided"
)
#expect(
request.body?.isHidden == nil,
body.isHidden == nil,
"It should have nil isHidden when not provided"
)
}

View file

@ -43,6 +43,7 @@ struct PURLsEnvironment {
)
}
// swiftlint:disable function_body_length
init(
networkServiceFactory: PURLsNetworkServiceFactoryProtocol,
persistenceServiceFactory: PURLsPersistenceServiceFactoryProtocol,
@ -123,4 +124,5 @@ struct PURLsEnvironment {
)
}
}
// swiftlint:enable function_body_length
}

View file

@ -9,8 +9,14 @@
private final class FakeClipboardService: ClipboardServiceProtocol {
func copy(_ string: String) {}
func copy(_ data: Data, type: String) {}
func copy(
_ string: String
) {}
func copy(
_ data: Data,
type: String
) {}
}
// MARK: - Public

View file

@ -13,10 +13,10 @@
count: Int,
in container: ModelContainer
) {
for i in 0..<count {
for index in 0..<count {
let purl = PURL(
name: "purl\(i)",
url: URL(string: "http://subdomain\(i).otavio.lol")!,
name: "purl\(index)",
url: URL(string: "http://subdomain\(index).otavio.lol")!,
address: "otaviocc"
)

View file

@ -17,9 +17,20 @@
}
}
func fetchPURLs(for address: String) {}
func addPURL(address: String, name: String, url: String) {}
func deletePURL(address: String, name: String) async throws {}
func fetchPURLs(
for address: String
) {}
func addPURL(
address: String,
name: String,
url: String
) {}
func deletePURL(
address: String,
name: String
) async throws {}
}
// MARK: - Public

View file

@ -37,8 +37,17 @@
// MARK: - Public
func fetchPURLs() {}
func addPURL(address: String, name: String, url: String) {}
func deletePURL(address: String, name: String) async throws {}
func addPURL(
address: String,
name: String,
url: String
) {}
func deletePURL(
address: String,
name: String
) async throws {}
}
// MARK: - Public

View file

@ -3,5 +3,6 @@ import XCTest
final class PURLsTests: XCTestCase {
// swiftlint:disable:next empty_xctest_method
func testExample() throws {}
}

View file

@ -43,6 +43,7 @@ struct PastebinEnvironment {
)
}
// swiftlint:disable function_body_length
init(
networkServiceFactory: PastebinNetworkServiceFactoryProtocol,
persistenceServiceFactory: PastebinPersistenceServiceFactoryProtocol,
@ -123,4 +124,5 @@ struct PastebinEnvironment {
)
}
}
// swiftlint:enable function_body_length
}

View file

@ -10,7 +10,11 @@
private final class FakeClipboardService: ClipboardServiceProtocol {
func copy(_ string: String) {}
func copy(_ data: Data, type: String) {}
func copy(
_ data: Data,
type: String
) {}
}
// MARK: - Public

View file

@ -12,13 +12,13 @@
count: Int,
in container: ModelContainer
) {
for i in 0..<count {
for index in 0..<count {
let paste = Paste(
title: "paste\(i).md",
title: "paste\(index).md",
content: "hello, world!",
timestamp: 123_123_123,
address: "otaviocc",
listed: i % 2 == 0
listed: index % 2 == 0
)
container.mainContext.insert(

View file

@ -17,9 +17,21 @@
}
}
func fetchPastes(for address: String) async throws {}
func createOrUpdatePaste(address: String, title: String, content: String, listed: Bool) async throws {}
func deletePaste(address: String, title: String) async throws {}
func fetchPastes(
for address: String
) async throws {}
func createOrUpdatePaste(
address: String,
title: String,
content: String,
listed: Bool
) async throws {}
func deletePaste(
address: String,
title: String
) async throws {}
}
// MARK: - Public

View file

@ -37,8 +37,18 @@
// MARK: - Public
func fetchPastes() async throws {}
func createOrUpdatePaste(address: String, title: String, content: String, isListed: Bool) async throws {}
func deletePaste(address: String, title: String) async throws {}
func createOrUpdatePaste(
address: String,
title: String,
content: String,
isListed: Bool
) async throws {}
func deletePaste(
address: String,
title: String
) async throws {}
}
// MARK: - Public

View file

@ -3,5 +3,6 @@ import XCTest
final class PastebinTests: XCTestCase {
// swiftlint:disable:next empty_xctest_method
func testExample() throws {}
}

View file

@ -42,6 +42,7 @@ struct PicsEnvironment {
)
}
// swiftlint:disable function_body_length
init(
networkServiceFactory: PicsNetworkServiceFactoryProtocol,
persistenceServiceFactory: PicsPersistenceServiceFactoryProtocol,
@ -119,4 +120,5 @@ struct PicsEnvironment {
)
}
}
// swiftlint:enable function_body_length
}

View file

@ -9,8 +9,14 @@
private final class FakeClipboardService: ClipboardServiceProtocol {
func copy(_ string: String) {}
func copy(_ data: Data, type: String) {}
func copy(
_ string: String
) {}
func copy(
_ data: Data,
type: String
) {}
}
// MARK: - Public

View file

@ -12,8 +12,15 @@
// MARK: - Public
func fetchPictures(for address: String) async throws -> [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,

View file

@ -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,

View file

@ -13,9 +13,9 @@
count: Int = 3,
in container: ModelContainer
) {
for i in 0..<count {
for index in 0..<count {
SomePicture.makePicture(
created: Double(i * i),
created: Double(index * index),
in: container
)
}

View file

@ -98,12 +98,13 @@ struct EditPictureView: 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)
}
}
}
)
}
}
@ -113,12 +114,13 @@ struct EditPictureView: 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)
}
}
}
)
}
}

View file

@ -21,7 +21,7 @@ struct PicturesListView: View {
_pictures = .init(viewModel.fetchDescriptor())
}
// MARK; - Public
// MARK: - Public
var body: some View {
Group {

View file

@ -209,12 +209,13 @@ struct UploadView: View {
if !viewModel.suggestedTags.isEmpty {
TagListView(
tags: viewModel.suggestedTags,
helpText: { "Add existing tag '\($0)'" }
) { tag in
withAnimation(.easeInOut(duration: 0.2)) {
viewModel.addTag(tag)
helpText: { "Add existing tag '\($0)'" },
action: { tag in
withAnimation(.easeInOut(duration: 0.2)) {
viewModel.addTag(tag)
}
}
}
)
}
}
@ -224,12 +225,13 @@ struct UploadView: View {
TagListView(
tags: viewModel.tags,
style: .remove,
helpText: { "Remove tag '\($0)'" }
) { tag in
withAnimation(.easeInOut(duration: 0.2)) {
viewModel.removeTag(tag)
helpText: { "Remove tag '\($0)'" },
action: { tag in
withAnimation(.easeInOut(duration: 0.2)) {
viewModel.removeTag(tag)
}
}
}
)
}
}

View file

@ -42,6 +42,7 @@ struct StatusEnvironment {
)
}
// swiftlint:disable function_body_length
init(
repositoryFactory: StatusRepositoryFactoryProtocol,
networkServiceFactory: StatusNetworkServiceFactoryProtocol,
@ -120,4 +121,5 @@ struct StatusEnvironment {
)
}
}
// swiftlint:enable function_body_length
}

View file

@ -9,8 +9,14 @@
private final class FakeClipboardService: ClipboardServiceProtocol {
func copy(_ string: String) {}
func copy(_ data: Data, type: String) {}
func copy(
_ string: String
) {}
func copy(
_ data: Data,
type: String
) {}
}
// MARK: - Public

View file

@ -12,11 +12,11 @@
count: Int,
in container: ModelContainer
) {
for i in 0..<count {
for index in 0..<count {
let status = Status(
username: "user\(i)",
statusID: "(i)",
timestamp: Double(i),
username: "user\(index)",
statusID: "(index)",
timestamp: Double(index),
icon: "🤣",
content: "Nulla purus urna, bibendum nec purus."
)

View file

@ -45,9 +45,10 @@ public struct StatusApp: View {
ToolbarItemGroup {
SelectionToolbarItem(
options: StatusListFilter.allCases,
selection: $filter,
itemLabel: { $0.localizedTitle }
)
selection: $filter
) { label in
label.localizedTitle
}
.selectionToolbarItemStyle(FilterSelectionToolbarItemStyle())
}

View file

@ -43,7 +43,13 @@ struct ClipboardService: ClipboardServiceProtocol {
service.copy(string)
}
func copy(_ data: Data, type: String) {
service.copy(data, type: type)
func copy(
_ data: Data,
type: String
) {
service.copy(
data,
type: type
)
}
}

View file

@ -17,7 +17,10 @@
)
}
func copy(_ data: Data, type: String) {
func copy(
_ data: Data,
type: String
) {
NSPasteboard
.general
.clearContents()

View file

@ -42,6 +42,7 @@ struct WeblogEnvironment {
)
}
// swiftlint:disable function_body_length
init(
networkServiceFactory: WeblogNetworkServiceFactoryProtocol,
persistenceServiceFactory: WeblogPersistenceServiceFactoryProtocol,
@ -119,4 +120,5 @@ struct WeblogEnvironment {
)
}
}
// swiftlint:enable function_body_length
}

View file

@ -9,8 +9,14 @@
private final class FakeClipboardService: ClipboardServiceProtocol {
func copy(_ string: String) {}
func copy(_ data: Data, type: String) {}
func copy(
_ string: String
) {}
func copy(
_ data: Data,
type: String
) {}
}
// MARK: - Public

View file

@ -27,14 +27,14 @@
}
static func makeEntryResponses(count: Int = 5) -> [EntryResponse] {
(0..<count).map { i in
(0..<count).map { index in
makeEntryResponse(
id: "entry-\(i)",
location: "test-entry-\(i)",
date: Double(1_700_000_000 + (i * 86400)),
status: i % 3 == 0 ? "draft" : "published",
title: "Test Entry \(i)",
body: "Content for test entry \(i)"
id: "entry-\(index)",
location: "test-entry-\(index)",
date: Double(1_700_000_000 + (index * 86400)),
status: index % 3 == 0 ? "draft" : "published",
title: "Test Entry \(index)",
body: "Content for test entry \(index)"
)
}
}

View file

@ -12,14 +12,14 @@
count: Int,
in container: ModelContainer
) {
for i in 0..<count {
for index in 0..<count {
let entry = WeblogEntry(
id: "entry-\(i)",
title: "Test Entry \(i)",
body: "This is the body for test entry \(i). Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
date: Double(1_700_000_000 + (i * 86400)),
status: i % 3 == 0 ? "draft" : "published",
location: "test-entry-\(i)",
id: "entry-\(index)",
title: "Test Entry \(index)",
body: "This is the body for test entry \(index). Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
date: Double(1_700_000_000 + (index * 86400)),
status: index % 3 == 0 ? "draft" : "published",
location: "test-entry-\(index)",
address: "otaviocc"
)

View file

@ -12,11 +12,16 @@
// MARK: - Public
func fetchWeblogEntry(for address: String, entryID: String) async throws -> 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

View file

@ -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,

View file

@ -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)
}
}
}
)
}
}

View file

@ -21,7 +21,7 @@ struct WeblogEntriesListView: View {
_entries = .init(viewModel.fetchDescriptor())
}
// MARK; - Public
// MARK: - Public
var body: some View {
Group {

View file

@ -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
}

View file

@ -12,11 +12,11 @@
count: Int,
in container: ModelContainer
) {
for i in 0..<count {
for index in 0..<count {
let page = Webpage(
address: "otaviocc",
markdown: "Foobar \(i)",
timestamp: 123_123 * Double(i)
markdown: "Foobar \(index)",
timestamp: 123_123 * Double(index)
)
container.mainContext.insert(

View file

@ -17,8 +17,14 @@
}
}
func fetchWebpage(for address: String) async throws {}
func updateWebpage(address: String, content: String) async throws {}
func fetchWebpage(
for address: String
) async throws {}
func updateWebpage(
address: String,
content: String
) async throws {}
}
// MARK: - Public

View file

@ -37,7 +37,11 @@
// MARK: - Public
func fetchWebpage() async throws {}
func updateWebpage(address: String, content: String) async throws {}
func updateWebpage(
address: String,
content: String
) async throws {}
}
// MARK: - Public

View file

@ -3,5 +3,6 @@ import XCTest
final class WebpageTests: XCTestCase {
// swiftlint:disable:next empty_xctest_method
func testExample() throws {}
}