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