This document outlines identified issues and planned refactoring/optimization work.
Problem: AppState handles too many responsibilities:
- File scanning
- Git operations
- Time travel logic
- Activity monitoring
- Selection management
- File operations
- Search indexing
- Watchers management
Solution: Extract into focused service classes:
GitService- Git status, commit history, dirty detectionTimeTravelManager- Historical tree loading, caching, mode switchingActivityMonitor- File activity tracking, helicopter/beam triggers
Problem: Hard-coded values scattered throughout:
maxNodes = 300maxDepth = 2throttleInterval = 0.15- Various animation durations
- Heights, widths, speeds
Solution: Create Constants.swift with organized constants:
enum Constants {
enum Scanning {
static let maxNodes = 300
static let maxDepth = 2
}
enum Animation {
static let activityDuration: TimeInterval = 1.4
static let throttleInterval: TimeInterval = 0.15
}
// etc.
}Problem:
- Long functions with complex nesting
- Repeated tier-building logic
- Hard-coded building shape parameters
Solution:
- Extract
BuildingStylerfor shape/texture selection - Extract tier-building into reusable function
- Move constants to
LayoutRules.swift
Problem:
visualTopY(for:)appears in multiple places (MetalRenderer, elsewhere)rotationYForWedge()duplicated in RayTracer and MetalRenderer- Similar loop patterns for finding max height at X/Z location
Solution:
- Move shared geometry helpers to
GeometryHelpers.swift - Create extension on
CityBlockfor computed properties
Problem: MetalRenderer is too large, handling:
- Pipeline setup
- Building rendering
- Car paths and instances
- Plane paths and instances
- Explosions
- Helicopters
- Beams
- Signposts
- Picking
Solution: Extract managers:
VehiclePathManager- Car/plane path generationExplosionManager- Explosion particle state- Keep core rendering in MetalRenderer
Issue: rebuildInstancesUsingCache() called frequently
Fix: Only rebuild when blocks actually change, use dirty flags
Issue: O(n) search for blocks at same X/Z repeated many times Fix: Create spatial index (dictionary by grid position)
Issue: Status checked for all repos on every rescan Fix: Cache status with TTL, only refresh on FSEvents trigger
Issue: Full rebuild on every block set change Fix: Incremental updates when possible
// New file: Core/Services/GitService.swift
final class GitService {
func isGitRepository(at url: URL) -> Bool
func getStatus(at url: URL) async -> GitStatus
func getCommitHistory(at url: URL, limit: Int) async -> [GitCommit]
func getTreeAtCommit(_ commit: GitCommit, in url: URL) async -> FileNode?
}// New file: Core/Services/TimeTravelManager.swift
@MainActor
final class TimeTravelManager: ObservableObject {
@Published var mode: TimeTravelMode = .live
@Published var commitHistory: [GitCommit] = []
private var historicalTreeCache: [String: FileNode] = [:]
func loadHistory(for url: URL) async
func loadTreeForCommit(_ commit: GitCommit) async -> FileNode?
func clearCache()
}// New file: Core/Constants.swift
enum Constants {
enum Scanning { ... }
enum Layout { ... }
enum Animation { ... }
enum Camera { ... }
enum Vehicles { ... }
}// New file: Core/Renderer/GeometryHelpers.swift
func visualTopY(for block: CityBlock) -> Float
func rotationYForWedge(block: CityBlock, cameraYaw: Float) -> Float
func maxHeightAt(x: Float, z: Float, in blocks: [CityBlock]) -> Float// Extension in CityBlock.swift
extension CityBlock {
var visualTop: Float { ... }
var gridKey: GridKey { ... }
}
struct GridKey: Hashable {
let x: Int
let z: Int
}- Ensure existing tests pass
- Add integration tests for key workflows:
- Directory scanning produces expected blocks
- CityMapper layout is deterministic
- Selection sync works
- Git detection works
- Run all tests
- Manual verification:
- App launches
- Can navigate directories
- 3D view renders
- Selection works
- Git features work
- ✅ Document features (FEATURES.md)
- ✅ Document refactoring plan (this file)
- Add pre-refactoring tests
- Create Constants.swift (safe, additive)
- Extract GeometryHelpers (safe, no behavior change)
- Extract GitService (moderate risk)
- Extract TimeTravelManager (moderate risk)
- Simplify AppState (high impact)
- Optimize MetalRenderer (moderate risk)
- Final verification
-
Core/Constants.swift -
Core/Services/GitService.swift -
Core/Services/TimeTravelManager.swift -
Core/Renderer/GeometryHelpers.swift - Additional unit tests
-
AppState.swift- Remove extracted code, use new services -
CityMapper.swift- Use constants, simplify -
MetalRenderer.swift- Use GeometryHelpers -
RayTracer.swift- Use shared helpers -
CityBlock.swift- Add extensions