listless

A simple list app for Apple platforms
Log | Files | Refs | README | LICENSE

commit 754a85d44b2f535c1954eba9b4e040127aec683f
parent 0ea68133bf492fe7f8038b08e5073f1b2e2a995c
Author: Michael Camilleri <[email protected]>
Date:   Thu, 26 Feb 2026 07:47:53 +0900

Use protocol to reduce SourceKit warnings

Co-Authored-By: Codex GPT 5.3 <[email protected]>

Diffstat:
MListless.xcodeproj/project.pbxproj | 12++++++++++++
MListless/Extensions/TaskListView+Logic.swift | 2+-
MListless/Extensions/TaskListView+SyncUI.swift | 2+-
AListless/Helpers/TaskListTypes.swift | 11+++++++++++
AListless/Helpers/TaskListViewProtocol.swift | 15+++++++++++++++
DListless/Infrastructure/.gitkeep | 0
MListlessMac/Views/TaskListView.swift | 12+-----------
MListlessMac/Views/TaskRowView.swift | 4++--
MListlessiOS/Views/TaskListView.swift | 12+-----------
MListlessiOS/Views/TaskRowView.swift | 4++--
10 files changed, 46 insertions(+), 28 deletions(-)

diff --git a/Listless.xcodeproj/project.pbxproj b/Listless.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 0ACA67F6578EFF181EE5C9A7 /* TaskItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBB8A3BEB346267B30B4675F /* TaskItem.swift */; }; 0F12D56A528FCBF8A67864CB /* TappableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6ECE0E961F87BA32FA87BF90 /* TappableTextField.swift */; }; 11AA75BE98CFBE44AEAB7100 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9404C09EE1A4D91DFF338464 /* Media.xcassets */; }; + 1477B460E3DEFA3FDB7DA65B /* TaskListTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F9754B32B2FD5AF8552BC85 /* TaskListTypes.swift */; }; 172F2DD978988E207610055F /* KeyboardNavigationModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4669414A460FC0758D5B49A8 /* KeyboardNavigationModifier.swift */; }; 182D9FB61E3A3650D7D83D2A /* TaskRowSwipeGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 067D666DF0AF1C53404ECF7C /* TaskRowSwipeGesture.swift */; }; 1AA328A921EF8A7FDD03119A /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75B048B19C5219862BBED2E7 /* TestHelpers.swift */; }; @@ -17,6 +18,7 @@ 26F609518DE1055DF09B0159 /* TaskListView+NavigationHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712ADAD0FE66175DAA9A6D50 /* TaskListView+NavigationHeader.swift */; }; 2C53717A0EAFA39240615C9A /* TaskRowDragGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57C3CC81C4380EFAE4DB910 /* TaskRowDragGesture.swift */; }; 322EE74CBED492693B92AD59 /* TaskListView+Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A440253D045A0896D94ADD0 /* TaskListView+Toolbar.swift */; }; + 365FDEE6823D7A114F3FB12A /* TaskListViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C018476BD91B73870244B9 /* TaskListViewProtocol.swift */; }; 3ABE52A15C2059D8D5570528 /* TaskStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3DEE364304587D280C5672 /* TaskStore.swift */; }; 3D1F551A03B97ECF4E3DC8B0 /* TaskRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E06485DBE35B60868E14202A /* TaskRowView.swift */; }; 4E5A0A02121E02124F80E320 /* TaskListView+SyncUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7BD42B1E3C71333FA24893 /* TaskListView+SyncUI.swift */; }; @@ -38,6 +40,7 @@ 889DCB2BB3C01DDA281EA81A /* CloudKitSyncMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E3E82F6093EEFC94A41FED9 /* CloudKitSyncMonitor.swift */; }; 8A8D164CAA7B95B4C7435A7C /* TaskListView+PullToClear.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A6D74EDDD7E3FC150064FB5 /* TaskListView+PullToClear.swift */; }; 91EDF52C7C5C0B35E9D8B51E /* TaskStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3DEE364304587D280C5672 /* TaskStore.swift */; }; + 93275BD83342D6CE94272E6A /* TaskListTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F9754B32B2FD5AF8552BC85 /* TaskListTypes.swift */; }; 96617677059FABDBB80D642B /* Listless.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = C093494053E6C348F245D4EC /* Listless.xcdatamodeld */; }; 99977BFA37FBAAA49AF6B71E /* TaskStoreEdgeCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B2BBE01D99CDA278BCB9F49 /* TaskStoreEdgeCaseTests.swift */; }; 99D17075DA3F00F52A18BB4D /* AccentColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17DD7EDA74DAAFA27C84CA08 /* AccentColor.swift */; }; @@ -48,6 +51,7 @@ C1FE091454864C4BBBBEB077 /* TaskItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBB8A3BEB346267B30B4675F /* TaskItem.swift */; }; CAB42FAA253E2B347AB0594B /* CloudKitErrorClassifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68A677CC11ACE0BF743AFCE5 /* CloudKitErrorClassifier.swift */; }; CAD142ED738A83371DFF8F5B /* TaskListView+Logic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72B4668483D05A6ECA142B89 /* TaskListView+Logic.swift */; }; + CC99A96BBC089C423F582E4F /* TaskListViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C018476BD91B73870244B9 /* TaskListViewProtocol.swift */; }; DB5FF6C1AA57D4C9BDDD50FD /* ClickableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D640E7D21735C62A30A26DA4 /* ClickableTextField.swift */; }; DC73A39A269AB495BCE1AC48 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14858BDFD1FD5119F1F24A6 /* PersistenceController.swift */; }; DEE187A790A4058FE4AFDB2E /* PlatformTextFieldWidthModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8448C5778F75F52719114AF /* PlatformTextFieldWidthModifier.swift */; }; @@ -98,6 +102,7 @@ 68A677CC11ACE0BF743AFCE5 /* CloudKitErrorClassifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitErrorClassifier.swift; sourceTree = "<group>"; }; 6E3E82F6093EEFC94A41FED9 /* CloudKitSyncMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitSyncMonitor.swift; sourceTree = "<group>"; }; 6ECE0E961F87BA32FA87BF90 /* TappableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TappableTextField.swift; sourceTree = "<group>"; }; + 6F9754B32B2FD5AF8552BC85 /* TaskListTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskListTypes.swift; sourceTree = "<group>"; }; 712ADAD0FE66175DAA9A6D50 /* TaskListView+NavigationHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TaskListView+NavigationHeader.swift"; sourceTree = "<group>"; }; 72B4668483D05A6ECA142B89 /* TaskListView+Logic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TaskListView+Logic.swift"; sourceTree = "<group>"; }; 74255E6B6C40899E9B17D927 /* .gitkeep */ = {isa = PBXFileReference; path = .gitkeep; sourceTree = "<group>"; }; @@ -121,6 +126,7 @@ C9B14DC786A336008AAB78EE /* .gitkeep */ = {isa = PBXFileReference; path = .gitkeep; sourceTree = "<group>"; }; CB43816B8E7F083A2AD07F28 /* TaskListView+Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TaskListView+Toolbar.swift"; sourceTree = "<group>"; }; D10B5491A53E77C80F8F75CD /* .gitkeep */ = {isa = PBXFileReference; path = .gitkeep; sourceTree = "<group>"; }; + D2C018476BD91B73870244B9 /* TaskListViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskListViewProtocol.swift; sourceTree = "<group>"; }; D2D9CDDA8913CD116FB4DA74 /* TaskListView+PullGestures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TaskListView+PullGestures.swift"; sourceTree = "<group>"; }; D57C3CC81C4380EFAE4DB910 /* TaskRowDragGesture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskRowDragGesture.swift; sourceTree = "<group>"; }; D640E7D21735C62A30A26DA4 /* ClickableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClickableTextField.swift; sourceTree = "<group>"; }; @@ -202,6 +208,8 @@ children = ( 17DD7EDA74DAAFA27C84CA08 /* AccentColor.swift */, 4669414A460FC0758D5B49A8 /* KeyboardNavigationModifier.swift */, + 6F9754B32B2FD5AF8552BC85 /* TaskListTypes.swift */, + D2C018476BD91B73870244B9 /* TaskListViewProtocol.swift */, ); path = Helpers; sourceTree = "<group>"; @@ -475,10 +483,12 @@ 5761B201BF46FCA9C5C98CEF /* PlatformScrollIndicatorsModifier.swift in Sources */, DEE187A790A4058FE4AFDB2E /* PlatformTextFieldWidthModifier.swift in Sources */, C1FE091454864C4BBBBEB077 /* TaskItem.swift in Sources */, + 93275BD83342D6CE94272E6A /* TaskListTypes.swift in Sources */, E5878BAA0EA66A94440E2B0F /* TaskListView+Logic.swift in Sources */, 642151C8EEA34DAD76C49FA1 /* TaskListView+SyncUI.swift in Sources */, 322EE74CBED492693B92AD59 /* TaskListView+Toolbar.swift in Sources */, FDD09FECEED48EC9598538F4 /* TaskListView.swift in Sources */, + 365FDEE6823D7A114F3FB12A /* TaskListViewProtocol.swift in Sources */, 5035EC4C7518A5FF9AD454CA /* TaskRowDragGesture.swift in Sources */, 3D1F551A03B97ECF4E3DC8B0 /* TaskRowView.swift in Sources */, 3ABE52A15C2059D8D5570528 /* TaskStore.swift in Sources */, @@ -504,6 +514,7 @@ E47136CA7428927395D8C7C7 /* PullToCreate.swift in Sources */, 0F12D56A528FCBF8A67864CB /* TappableTextField.swift in Sources */, 0ACA67F6578EFF181EE5C9A7 /* TaskItem.swift in Sources */, + 1477B460E3DEFA3FDB7DA65B /* TaskListTypes.swift in Sources */, 82E75475E13FD847DDDC69D8 /* TaskListView+Drag.swift in Sources */, CAD142ED738A83371DFF8F5B /* TaskListView+Logic.swift in Sources */, 26F609518DE1055DF09B0159 /* TaskListView+NavigationHeader.swift in Sources */, @@ -513,6 +524,7 @@ 4E5A0A02121E02124F80E320 /* TaskListView+SyncUI.swift in Sources */, 77970A2323A598E830D95301 /* TaskListView+Toolbar.swift in Sources */, C169823665158AA347A63990 /* TaskListView.swift in Sources */, + CC99A96BBC089C423F582E4F /* TaskListViewProtocol.swift in Sources */, 2C53717A0EAFA39240615C9A /* TaskRowDragGesture.swift in Sources */, FF58A3AB2BCFFA42BF2F6413 /* TaskRowMetrics.swift in Sources */, 182D9FB61E3A3650D7D83D2A /* TaskRowSwipeGesture.swift in Sources */, diff --git a/Listless/Extensions/TaskListView+Logic.swift b/Listless/Extensions/TaskListView+Logic.swift @@ -1,6 +1,6 @@ import SwiftUI -extension TaskListView { +extension TaskListViewProtocol { // MARK: - Computed Properties diff --git a/Listless/Extensions/TaskListView+SyncUI.swift b/Listless/Extensions/TaskListView+SyncUI.swift @@ -5,7 +5,7 @@ import UIKit import AppKit #endif -extension TaskListView { +extension TaskListViewProtocol { @ViewBuilder var syncErrorBanner: some View { if let message = syncMonitor.transientErrorMessage { diff --git a/Listless/Helpers/TaskListTypes.swift b/Listless/Helpers/TaskListTypes.swift @@ -0,0 +1,11 @@ +import Foundation + +enum FocusField: Hashable { + case task(UUID) + case scrollView +} + +enum DragState: Equatable { + case idle + case dragging(id: UUID, order: [UUID]) +} diff --git a/Listless/Helpers/TaskListViewProtocol.swift b/Listless/Helpers/TaskListViewProtocol.swift @@ -0,0 +1,15 @@ +import CoreData +import SwiftUI + +@MainActor +protocol TaskListViewProtocol { + var tasks: FetchedResults<TaskItem> { get } + var store: TaskStore { get } + var syncMonitor: CloudKitSyncMonitor { get } + var managedObjectContext: NSManagedObjectContext { get } + var focusedField: FocusField? { get nonmutating set } + var selectedTaskID: UUID? { get nonmutating set } + var pendingFocus: FocusField? { get nonmutating set } + var dragState: DragState { get nonmutating set } + func didStartDrag() +} diff --git a/Listless/Infrastructure/.gitkeep b/Listless/Infrastructure/.gitkeep diff --git a/ListlessMac/Views/TaskListView.swift b/ListlessMac/Views/TaskListView.swift @@ -1,17 +1,7 @@ import SwiftUI import UniformTypeIdentifiers -struct TaskListView: View { - enum FocusField: Hashable { - case task(UUID) - case scrollView - } - - enum DragState: Equatable { - case idle - case dragging(id: UUID, order: [UUID]) - } - +struct TaskListView: View, TaskListViewProtocol { struct FocusStateData { var focusedField: FocusField? var selectedTaskID: UUID? diff --git a/ListlessMac/Views/TaskRowView.swift b/ListlessMac/Views/TaskRowView.swift @@ -13,7 +13,7 @@ struct TaskRowView: View { let onStartEdit: (UUID) -> Void let onEndEdit: (UUID, _ shouldCreateNewTask: Bool) -> Void let onPaste: (String) -> Void - @FocusState.Binding var focusedField: TaskListView.FocusField? + @FocusState.Binding var focusedField: FocusField? @State private var editingTitle: String = "" @State private var isCurrentlyEditing: Bool = false @@ -39,7 +39,7 @@ struct TaskRowView: View { index: Int = 0, totalTasks: Int = 1, isSelected: Bool, - focusedField: FocusState<TaskListView.FocusField?>.Binding, + focusedField: FocusState<FocusField?>.Binding, onToggle: @escaping (TaskItem) -> Void, onTitleChange: @escaping (TaskItem, String) -> Void, onDelete: @escaping (TaskItem) -> Void, diff --git a/ListlessiOS/Views/TaskListView.swift b/ListlessiOS/Views/TaskListView.swift @@ -1,16 +1,6 @@ import SwiftUI -struct TaskListView: View { - enum FocusField: Hashable { - case task(UUID) - case scrollView - } - - enum DragState: Equatable { - case idle - case dragging(id: UUID, order: [UUID]) - } - +struct TaskListView: View, TaskListViewProtocol { struct FocusStateData { var focusedField: FocusField? var selectedTaskID: UUID? diff --git a/ListlessiOS/Views/TaskRowView.swift b/ListlessiOS/Views/TaskRowView.swift @@ -13,7 +13,7 @@ struct TaskRowView: View { let onSelect: (UUID) -> Void let onStartEdit: (UUID) -> Void let onEndEdit: (UUID, _ shouldCreateNewTask: Bool) -> Void - @FocusState.Binding var focusedField: TaskListView.FocusField? + @FocusState.Binding var focusedField: FocusField? @State private var editingTitle: String = "" @State private var isCurrentlyEditing: Bool = false @@ -29,7 +29,7 @@ struct TaskRowView: View { totalTasks: Int = 1, isSelected: Bool, isDragging: Binding<Bool> = .constant(false), - focusedField: FocusState<TaskListView.FocusField?>.Binding, + focusedField: FocusState<FocusField?>.Binding, onToggle: @escaping (TaskItem) -> Void, onTitleChange: @escaping (TaskItem, String) -> Void, onDelete: @escaping (TaskItem) -> Void,