From a41d541c03d9995ba21ec1b71438e1bf69cf955c Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Mon, 24 Aug 2026 08:19:39 +0530 Subject: [PATCH 01/55] feat: initialize MacSweep project with core scanner, cleaner architecture, and UI feature modules --- .gitignore | 0 CHANGELOG.md | 31 +++++++ CODE_OF_CONDUCT.md | 53 ++++++++++++ CONTRIBUTING.md | 45 ++++++++++ LICENSE | 190 +++++++++++++++++++++++++++++++++++++++++++ README.md | 84 +++++++++++++++++++ SECURITY.md | 46 +++++++++++ docs/architecture.md | 63 ++++++++++++++ docs/development.md | 143 ++++++++++++++++++++++++++++++++ docs/permissions.md | 83 +++++++++++++++++++ docs/privacy.md | 79 ++++++++++++++++++ docs/releases.md | 132 ++++++++++++++++++++++++++++++ docs/security.md | 28 +++++++ 13 files changed, 977 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 docs/architecture.md create mode 100644 docs/development.md create mode 100644 docs/permissions.md create mode 100644 docs/privacy.md create mode 100644 docs/releases.md create mode 100644 docs/security.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..13fb54d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog + +All notable changes to **MacSweep** will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +--- + +## [Unreleased] + +### Planned +- Storage Analyzer interactive tree component (`v0.2.0`) +- Large File Finder threshold scanning (`v0.2.0`) +- Duplicate File Detection engine (`v0.3.0`) +- Application Uninstaller leftover finder (`v0.4.0`) + +--- + +## [0.1.0-beta] - 2026-08-24 + +### Added +- Initial OpenCorex project structure and Xcode workspace layout. +- Decoupled `Scanner`, `Cleaner`, and `Safety` engine architecture. +- Core safety rules database (`ProtectedPaths.swift`) enforcing zero system file deletion. +- Native macOS `FileManager.trashItem` implementation. +- Developer cache scanners for Xcode DerivedData, Gradle, Node/npm, Homebrew, and Docker. +- Unified SwiftUI `DashboardView`, `SmartScanView`, and `SettingsView`. +- Build, test, notarization, and DMG packaging automation scripts (`scripts/`). +- GitHub Actions CI/CD workflows (`ci.yml`, `test.yml`, `release.yml`). +- Comprehensive technical documentation (`docs/architecture.md`, `docs/permissions.md`, `docs/development.md`, `docs/releases.md`, `docs/privacy.md`). diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7698b44 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,53 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +--- + +## Our Standards + +Examples of behavior that contributes to a positive environment for our community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +--- + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. + +--- + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. + +--- + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the OpenCorex leadership team at **conduct@opencorex.org**. All complaints will be reviewed and investigated promptly and fairly. + +--- + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3f3b544 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,45 @@ +# Contributing to MacSweep + +Thank you for your interest in contributing to **MacSweep** under the **OpenCorex** organization! We welcome pull requests, bug reports, feature requests, and security improvements from developers of all experience levels. + +--- + +## 1. Code of Conduct + +All contributors are expected to adhere to our [Code of Conduct](CODE_OF_CONDUCT.md). Please read it before participating in discussions or submitting pull requests. + +--- + +## 2. How to Contribute + +### 2.1 Reporting Bugs +* Search existing [GitHub Issues](https://github.com/OpenCorex/macsweep/issues) to verify the bug hasn't been reported. +* Open a new bug report using our `.github/ISSUE_TEMPLATE/bug_report.yml` template. +* Include detailed steps to reproduce, macOS version, and relevant `OSLog` diagnostics (never post personal file paths). + +### 2.2 Suggesting Features +* Open a new feature request using `.github/ISSUE_TEMPLATE/feature_request.yml`. +* Describe the feature, why it is useful, and how it aligns with MacSweep's core goals of safety and transparency. + +### 2.3 Submitting Pull Requests +1. Fork the repository and create a feature branch (`git checkout -b feature/amazing-scanner`). +2. Follow our [Developer Guide](docs/development.md) for setting up Xcode and dependencies. +3. Ensure all code passes **SwiftLint**: `swiftlint lint`. +4. Ensure all unit and integration tests pass: `./scripts/test.sh`. +5. Write unit tests covering any new scanner logic or safety rules. +6. Submit a clean Pull Request targeting the `develop` branch with a clear summary of changes. + +--- + +## 3. Core Development Principles + +* **Safety Above All:** Never bypass path safety checks or attempt silent deletions. +* **No Telemetry:** Never introduce tracking SDKs, analytics pings, or network calls for file metadata. +* **Native Swift & SwiftUI:** Use standard Apple frameworks (Swift Concurrency, Foundation, SwiftUI, OSLog). +* **Test Coverage:** All new features must include unit tests. + +--- + +## 4. Community & Support + +Join the conversation on [GitHub Discussions](https://github.com/OpenCorex/macsweep/discussions) or contact the maintainers at **dev@opencorex.org**. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c58d257 --- /dev/null +++ b/LICENSE @@ -0,0 +1,190 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2026 OpenCorex + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a8cf7a7 --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ +# MacSweep + +

+ MacSweep Logo +

+ +

+ An open-source, transparent, and privacy-first macOS system cleaner, storage analyzer, and developer-cache manager. +

+ +

+ CI Status + License + Swift Version + Platform + Direct Download +

+ +--- + +## Key Features + +* 🧹 **Smart Scan:** Unified discovery of system caches, application logs, temporary files, and Trash items. +* 🛠️ **Developer Cache Cleaner:** Targeted cleaning of Xcode DerivedData, SPM caches, CocoaPods, Gradle, Android build caches, npm, pnpm, Yarn, Bun, Homebrew, and Docker artifacts. +* 📊 **Interactive Storage Analyzer:** Visual folder tree and disk usage breakdown. +* 📁 **Large File Finder:** Discover large files over configurable thresholds (500 MB to 10 GB+). +* 🔍 **Safe Duplicate Detection:** Multi-stage content hash engine for discovering duplicate copies safely. +* 📦 **App Uninstaller:** Complete leftover file discovery for uninstalled applications. +* 🔒 **Safety First:** Hardcoded system safeguards, symlink resolution, zero automated background deletions, and explicit Trash relocation. +* 🛡️ **Privacy First:** 100% local scanning, zero telemetry, zero analytics tracking, zero cloud server uploads. + +--- + +## Installation & Distribution + +MacSweep is distributed as a **Direct Download** package signed with Apple Developer ID certificates and notarized by Apple. + +### Direct Download +Download the latest Developer ID signed and Apple-notarized `.dmg` installer from [GitHub Releases](https://github.com/OpenCorex/macsweep/releases). + +1. Download `MacSweep.dmg`. +2. Open the DMG file and drag **MacSweep** into your `Applications` folder. +3. Launch MacSweep and grant Full Disk Access when prompted. + +### Building From Source +```bash +git clone https://github.com/OpenCorex/macsweep.git +cd macsweep +open MacSweep.xcodeproj +``` +Select the `MacSweep` scheme in Xcode and press `Cmd + R`. + +--- + +## Architecture & Safety + +MacSweep decouples file discovery from deletion: + +``` +[ Scanner Engine ] ──▶ [ Safety Validator ] ──▶ [ User Review ] ──▶ [ Trash Service ] +``` + +* **Scanner:** Read-only path enumerator. Cannot delete files. +* **Safety Validator:** Validates candidate paths against system boundaries (`/System`, `/usr`, `/Library/Keychains`) and verifies canonical symlinks. +* **Trash Service:** Moves approved files to the macOS Trash using native `FileManager` APIs. + +For full technical documentation, see [`docs/architecture.md`](docs/architecture.md) and [`docs/permissions.md`](docs/permissions.md). + +--- + +## Documentation + +* 📖 [System Architecture Specification](docs/architecture.md) +* 🔐 [Permissions & FDA Guide](docs/permissions.md) +* 🚀 [Developer Setup & Contributing Guide](docs/development.md) +* 📦 [Direct Release & Code Signing Guide](docs/releases.md) +* 🛡️ [Privacy Policy Statement](docs/privacy.md) +* 🔒 [Security & Vulnerability Disclosure](SECURITY.md) + +--- + +## License + +MacSweep is open-source software licensed under the **Apache License 2.0**. Developed with ❤️ under the **OpenCorex** organization. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..3e4e8d5 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,46 @@ +# Security Policy & Vulnerability Disclosure + +**Organization:** OpenCorex +**Project:** MacSweep (`OpenCorex/macsweep`) + +--- + +## 1. Supported Versions + +Security updates and safety patches are actively applied to the following versions of MacSweep: + +| Version | Supported | +| :--- | :--- | +| `1.x.x` | Yes | +| `0.1.x` (Beta MVP) | Yes | +| `< 0.1.0` | No | + +--- + +## 2. Reporting a Vulnerability + +The OpenCorex maintainers take security and user data safety extremely seriously. If you discover a security vulnerability, path traversal flaw, privilege escalation risk, or potential data loss scenario in MacSweep: + +**Please DO NOT open a public GitHub issue.** + +Instead, report vulnerabilities privately through one of the following channels: + +* **Email:** Send details to **security@opencorex.org** +* **GitHub Private Advisory:** Submit via [GitHub Security Advisories](https://github.com/OpenCorex/macsweep/security/advisories/new) + +### What to Include in Your Report +* A detailed description of the vulnerability. +* Steps to reproduce the issue cleanly. +* Potential security or safety impact (e.g., unexpected path deletion, symlink escape). +* Proof-of-concept script or test case if available. + +--- + +## 3. Response & Resolution Process + +1. **Acknowledgement:** We will acknowledge receipt of your security report within **24 hours**. +2. **Assessment:** The OpenCorex security team will investigate and validate the report within **72 hours**. +3. **Patch & Release:** A security patch will be developed, tested, and released as a hotfix version. +4. **Public Disclosure:** A coordinated public security advisory will be published detailing the fix. + +Thank you for helping keep MacSweep safe for the open-source community! diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..e2180c5 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,63 @@ +# MacSweep Architecture & Technical Specification +**Organization:** OpenCorex +**Project:** MacSweep (`OpenCorex/macsweep`) +**Platform:** macOS 14.0+ + +--- + +For the full detailed 31-section technical specification document, see the canonical specification document. + +## System Architecture Summary + +MacSweep uses a layered data-flow architecture: + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ SwiftUI View Layer │ +│ (DashboardView, SmartScanView, StorageAnalyzerView, SettingsView) │ +└───────────────────────────────────┬────────────────────────────────────┘ + │ Binds to State & Triggers Actions + ▼ +┌────────────────────────────────────────────────────────────────────────┐ +│ Feature ViewModels │ +│ (@Observable / @StateObject ViewModels handling UI state & flow) │ +└───────────────────────────────────┬────────────────────────────────────┘ + │ Invokes Domain Operations + ▼ +┌────────────────────────────────────────────────────────────────────────┐ +│ Service Layer │ +│ (ApplicationService, DuplicateDetectionService, LargeFileService) │ +└───────────────────────────────────┬────────────────────────────────────┘ + │ Calls Core Engines & Validators + ▼ +┌────────────────────────────────────────────────────────────────────────┐ +│ Core Subsystems │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │ +│ │ Scanner │──▶│ Safety │──▶│ Cleaner │ │ Storage │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ └───────────┘ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ FileSystem │ │ Permissions │ │ Utilities │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +└───────────────────────────────────┬────────────────────────────────────┘ + │ Low-level system I/O + ▼ +┌────────────────────────────────────────────────────────────────────────┐ +│ macOS Native APIs │ +│ (FileManager, URLResourceValues, OSLog, Security Scopes, SMAppService)│ +└────────────────────────────────────────────────────────────────────────┘ +``` + +### Core Separation Principle +1. **Scanner Subsystem:** Read-only path discovery and calculation. Has zero mutation/deletion capabilities. +2. **Safety Subsystem:** Validates target paths against system bounds, protected paths, and symlink escape checks. +3. **Cleaner Subsystem:** Receives user-confirmed, safety-checked items and performs safe Trash relocation or deletion. + +### Core Modules Breakdown +- **`App`**: SwiftUI entrypoint, `AppDelegate`, DI container (`AppEnvironment`), global constants. +- **`Core/Scanner`**: Asynchronous file system enumerators & cache scanners (`CacheScanner`, `XcodeScanner`, `GradleScanner`, `NodeScanner`, `DockerScanner`, etc.). +- **`Core/Cleaner`**: Operational execution engine & Trash services (`CleanEngine`, `DeletionService`, `TrashService`). +- **`Core/Safety`**: Rules database & path traversal validation (`SafetyValidator`, `ProtectedPaths`, `SafetyPolicy`). +- **`Core/Storage`**: System disk statistics & tree folder size calculators (`DiskSpaceService`, `DirectorySizeCalculator`). +- **`Core/FileSystem`**: Low-level `FileManager` wrappers, metadata extraction, security-scoped bookmark handles. +- **`Core/Permissions`**: Full Disk Access validators & sandbox permission managers (`PermissionManager`, `FullDiskAccessManager`). +- **`Core/Utilities`**: Formatting, logging (`OSLog`), process execution helpers (`Logger`, `ByteFormatter`). diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..c3d0077 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,143 @@ +# MacSweep Development & Setup Guide + +**Organization:** OpenCorex +**Project:** MacSweep (`OpenCorex/macsweep`) +**Target Platform:** macOS 14.0+ + +--- + +## 1. Prerequisites & Environment Setup + +To build, run, and contribute to MacSweep, ensure your development environment satisfies the following prerequisites: + +* **Hardware:** Apple Silicon (M1/M2/M3/M4) or Intel Mac running macOS 14.0 (Sonoma) or later. +* **Xcode:** Xcode 15.4 or Xcode 16.0+ installed from the Mac App Store or Apple Developer Portal. +* **Command Line Tools:** Xcode Command Line Tools installed via `xcode-select --install`. +* **Swift Toolchain:** Swift 5.10+ included with Xcode. +* **Linting:** SwiftLint installed via Homebrew (`brew install swiftlint`). +* **Packaging (Optional):** `create-dmg` tool installed via Homebrew (`brew install create-dmg`) for testing local DMG generation. + +--- + +## 2. Cloning & Workspace Structure + +Clone the repository directly from GitHub: + +```bash +git clone https://github.com/OpenCorex/macsweep.git +cd macsweep +``` + +### Directory Anatomy +``` +macsweep/ +├── MacSweep.xcodeproj # Primary Xcode Workspace Project +├── MacSweep/ # Application Source Code +│ ├── App/ # Entrypoint & Environment +│ ├── Core/ # Scanner, Cleaner, Safety, Storage Logic +│ ├── Models/ # Swift Value Types & Data Transfer Objects +│ ├── Services/ # Domain Business Logic Services +│ ├── Features/ # SwiftUI Feature Views & ViewModels +│ ├── UI/ # Design System & Theme Components +│ └── Resources/ # Assets & Strings +├── MacSweepTests/ # Unit & Integration Test Suites +├── MacSweepUITests/ # UI Automation Test Suites +├── scripts/ # Build, Test, Notarization & Release Automation +└── docs/ # Project Technical Specifications +``` + +--- + +## 3. Building & Running Locally + +### 3.1 Using Xcode IDE +1. Open `MacSweep.xcodeproj` in Xcode: + ```bash + open MacSweep.xcodeproj + ``` +2. Select the **MacSweep** target and scheme. +3. Select **My Mac** as the run destination. +4. Press `Cmd + R` to compile and launch MacSweep. + +### 3.2 Using Command Line Automation +You can build the project from the command line using the provided build scripts: + +```bash +# Make build script executable +chmod +x scripts/build.sh + +# Run compilation script +./scripts/build.sh +``` + +Alternatively, invoke `xcodebuild` directly: + +```bash +xcodebuild -project MacSweep.xcodeproj \ + -scheme MacSweep \ + -configuration Debug \ + -destination 'platform=macOS' \ + build +``` + +--- + +## 4. Running Tests + +### 4.1 Running Unit & Integration Tests +Execute all test suites from Xcode using `Cmd + U`, or run the automated test script: + +```bash +chmod +x scripts/test.sh +./scripts/test.sh +``` + +Command line execution via `xcodebuild`: + +```bash +xcodebuild test \ + -project MacSweep.xcodeproj \ + -scheme MacSweep \ + -destination 'platform=macOS' \ + -enableCodeCoverage YES +``` + +--- + +## 5. Code Style & Linting + +MacSweep enforces strict Swift styling rules using **SwiftLint**. + +* To view lint errors in Xcode, build the project (SwiftLint automatically runs via a build phase script). +* To run linting manually from the terminal: + ```bash + swiftlint lint + ``` +* To automatically fix auto-correctable formatting rules: + ```bash + swiftlint --fix + ``` + +--- + +## 6. Architecture & Coding Conventions + +### Concurrency Rules +* Always use **Swift Concurrency** (`async/await`, `AsyncSequence`, `Actor`, `TaskGroup`) rather than legacy `DispatchQueue` callbacks. +* Mark ViewModels and UI state bindings with `@MainActor`. +* Keep background scanning engines on non-blocking actors (`actor ScanEngine`). + +### Safety Policy +* **Never** call shell executables (`rm -rf`) via `Process`. +* Always validate paths through `SafetyValidator.shared.validate(url:)` before initiating deletions. +* Ensure all user deletions route through `FileManager.default.trashItem(at:resultingItemURL:)`. + +--- + +## 7. Troubleshooting + +| Issue | Cause | Solution | +| :--- | :--- | :--- | +| **Permission Denied in Tests** | Test suite scanning protected user paths | Ensure tests use `FileManager.default.temporaryDirectory` mock fixtures. | +| **SwiftLint Command Not Found** | SwiftLint is not installed in `/opt/homebrew/bin` | Run `brew install swiftlint`. | +| **Signing Error on Local Build** | Missing Developer ID signing identity | Set Xcode **Signing & Capabilities > Team** to **None (Sign to Run Locally)** for local testing. | diff --git a/docs/permissions.md b/docs/permissions.md new file mode 100644 index 0000000..13e0074 --- /dev/null +++ b/docs/permissions.md @@ -0,0 +1,83 @@ +# macOS Permissions Technical Specification + +**Project:** MacSweep (`OpenCorex/macsweep`) +**Organization:** OpenCorex +**Distribution Channel:** Direct Download Only (Developer ID Signed & Apple Notarized) +**Target Platform:** macOS 14.0 (Sonoma) and later +**Document Status:** Production Reference + +--- + +## 1. Overview + +MacSweep is distributed exclusively as a **Direct Download** application (via GitHub Releases and the OpenCorex project portal). It is signed with an Apple **Developer ID Application** certificate, compiled with **Hardened Runtime** enabled, and notarized by Apple's Notary Service. + +Because MacSweep performs system cleaning, developer-cache management, storage analysis, and log cleanup across user domain and system-adjacent directories, it relies on **Full Disk Access (FDA)** and explicit user-granted permissions. + +MacSweep **never** attempts to bypass macOS security controls, disable System Integrity Protection (SIP), or elevate privileges silently. + +--- + +## 2. Permission Architecture + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ MacSweep Direct Download Model │ +├─────────────────────────────────────────────────────────────────────────┤ +│ • Distribution: GitHub Releases / OpenCorex Portal (.dmg installer) │ +│ • Code Signing: Developer ID Application Certificate │ +│ • Security Enforcement: Hardened Runtime Enabled │ +│ • Gatekeeper Compliance: Apple Notarized & Stapled │ +│ • Disk Access Scope: Full Disk Access (FDA) & User Permission Prompts │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 3. Full Disk Access (FDA) + +### 3.1 Why Full Disk Access is Required +To scan and clean protected location categories—such as Xcode DerivedData (`~/Library/Developer/Xcode`), system logs (`/private/var/log`), containerized application caches (`~/Library/Containers`), and third-party developer tool stores—macOS requires the user to grant Full Disk Access to MacSweep. + +### 3.2 Programmatic FDA Detection +MacSweep detects Full Disk Access status by attempting to inspect a known system-protected path: + +```swift +import Foundation +import AppKit + +public final class FullDiskAccessManager: Sendable { + public static let shared = FullDiskAccessManager() + + /// System path used to test Full Disk Access grant + private let testPath = "/Library/Preferences/com.apple.TimeMachine.plist" + + /// Returns `true` if Full Disk Access is granted by the user + public var hasFullDiskAccess: Bool { + return FileManager.default.isReadableFile(atPath: testPath) + } + + /// Opens macOS System Settings directly to Privacy & Security > Full Disk Access + public func openSystemSettingsFDA() { + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") { + NSWorkspace.shared.open(url) + } + } +} +``` + +### 3.3 User Guidance & Onboarding Workflow +1. Upon initial launch, MacSweep checks `FullDiskAccessManager.shared.hasFullDiskAccess`. +2. If FDA is missing, MacSweep displays an intuitive permissions banner explaining why access is necessary. +3. Clicking **Grant Full Disk Access** launches **System Settings > Privacy & Security > Full Disk Access**. +4. MacSweep listens for app focus changes (`NSApplication.didBecomeActiveNotification`) to update permission state dynamically without requiring app restart. + +--- + +## 4. Permission Failure & Graceful Fallback + +If Full Disk Access is not granted or a specific path returns permission denied: +* MacSweep **never crashes** or displays uncaught runtime exceptions. +* Diagnostics are logged via `OSLog` (`Logger.permissions`). +* Inaccessible folders are marked with a `Permission Needed` badge and an in-line resolution button. +* Scans proceed safely across all accessible paths while skipping restricted paths cleanly. diff --git a/docs/privacy.md b/docs/privacy.md new file mode 100644 index 0000000..a8e7201 --- /dev/null +++ b/docs/privacy.md @@ -0,0 +1,79 @@ +# MacSweep Privacy Architecture & Policy + +**Organization:** OpenCorex +**Project:** MacSweep (`OpenCorex/macsweep`) + +--- + +## 1. Core Privacy Commitments + +MacSweep is engineered under a **Strict Zero-Knowledge, Privacy-First Architecture**. We believe system maintenance utilities should respect user privacy and never monitor, track, or collect personal files. + +### The 5 Privacy Pillars of MacSweep +1. **100% Local Scanning:** All disk discovery, file size calculations, hash checks, and cache cleanup operations execute exclusively on your local Apple Silicon or Intel Mac CPU. +2. **Zero Network Telemetry:** MacSweep contains **no analytics frameworks**, tracking SDKs, or background pinging services. +3. **No File Transmission:** Your file names, folder structures, file contents, file hashes, and disk metrics **never** leave your machine. +4. **No Cloud Dependencies:** MacSweep does not require an account, registration, login, or cloud connection to function. +5. **Transparent Open Source Code:** Every line of code is open source under the Apache 2.0 license, allowing independent security auditing. + +--- + +## 2. Network Activity Disclosure + +MacSweep performs network connections **only** in the following explicit scenarios: + +* **Software Update Check (Direct Release Build Only):** + - **Purpose:** Checks if a newer version of MacSweep is available on GitHub Releases. + - **Destination:** `https://api.github.com/repos/OpenCorex/macsweep/releases/latest` + - **Data Transmitted:** Standard HTTP User-Agent requesting JSON release metadata. Zero user identifier, hardware serial, or system information is sent. + - **User Control:** Updates can be checked manually or toggled off in **Settings > General**. + +--- + +## 3. Apple Privacy Manifest Compliance (`PrivacyInfo.xcprivacy`) + +MacSweep ships with a fully declared Apple Privacy Manifest (`PrivacyInfo.xcprivacy`): + +```xml + + + + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + NSPrivacyCollectedDataTypes + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryDiskSpace + NSPrivacyAccessedAPITypeReasons + + 85F4.1 + + + + + +``` + +### Explanations of Accessed APIs +* **File Timestamps (`C617.1`):** Required to display file last-modified dates to the user in Large Files and Duplicates finder workflows. +* **Disk Space (`85F4.1`):** Required to calculate total, used, and available disk capacity displayed on the Dashboard. + +--- + +## 4. Contact & Security Audits + +If you have questions regarding MacSweep's privacy architecture or wish to conduct an independent security review, please contact **privacy@opencorex.org**. diff --git a/docs/releases.md b/docs/releases.md new file mode 100644 index 0000000..d3b1642 --- /dev/null +++ b/docs/releases.md @@ -0,0 +1,132 @@ +# MacSweep Direct Release & Code Signing Specification + +**Organization:** OpenCorex +**Project:** MacSweep (`OpenCorex/macsweep`) +**Distribution Channel:** Direct Download Only (Developer ID Signed & Apple Notarized) +**Target Platform:** macOS 14.0+ + +--- + +## 1. Distribution Architecture + +MacSweep is distributed exclusively as a **Direct Download** package via OpenCorex GitHub Releases. + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ Direct Release Security Standards │ +├────────────────────────────────────────────────────────────────────────┤ +│ • Code Signing: Developer ID Application Certificate │ +│ • Runtime Security: Hardened Runtime Enabled (`-options runtime`) │ +│ • Packaging: Branded `.dmg` installer with drag-to-Applications layout │ +│ • Apple Notarization: Verified via `xcrun notarytool` │ +│ • Stapling: Embedded ticket via `xcrun stapler staple` for Gatekeeper │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 2. Release Automation Pipeline + +``` +[ Published Git Tag (v*) ] + │ + ▼ +[ GitHub Actions Triggered: .github/workflows/release.yml ] + │ + ▼ +┌─────────────────────────────────────────┐ +│ Step 1: Compile & Xcode Release Archive │ +│ xcodebuild archive -scheme MacSweep │ +└────────────────────┬────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────┐ +│ Step 2: Export Developer ID Signed Build│ +│ xcodebuild -exportArchive │ +└────────────────────┬────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────┐ +│ Step 3: Package Custom Disk Image (.dmg)│ +│ ./scripts/create-dmg.sh │ +└────────────────────┬────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────┐ +│ Step 4: Submit to Apple Notary Service │ +│ ./scripts/notarize.sh submit │ +└────────────────────┬────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────┐ +│ Step 5: Staple Ticket & Verify DMG │ +│ xcrun stapler staple MacSweep.dmg │ +└────────────────────┬────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────┐ +│ Step 6: Publish GitHub Release │ +│ Upload MacSweep.dmg & SHA256 Checksums │ +└─────────────────────────────────────────┘ +``` + +--- + +## 3. Automation Scripts + +### 3.1 DMG Packaging (`scripts/create-dmg.sh`) +```bash +#!/usr/bin/env bash +set -euo pipefail + +APP_PATH="build/Export/Direct/MacSweep.app" +DMG_PATH="build/Release/MacSweep.dmg" + +mkdir -p build/Release + +create-dmg \ + --volname "MacSweep Installer" \ + --background "Distribution/DMG/background.png" \ + --window-pos 200 120 \ + --window-size 660 400 \ + --icon-size 100 \ + --icon "MacSweep.app" 180 170 \ + --hide-extension "MacSweep.app" \ + --app-drop-link 480 170 \ + "$DMG_PATH" \ + "$APP_PATH" +``` + +### 3.2 Apple Notarization (`scripts/notarize.sh`) +```bash +#!/usr/bin/env bash +set -euo pipefail + +DMG_PATH="build/Release/MacSweep.dmg" + +echo "Submitting $DMG_PATH to Apple Notary Service..." + +xcrun notarytool submit "$DMG_PATH" \ + --key-id "$NOTARIZATION_KEY_ID" \ + --issuer "$NOTARIZATION_ISSUER_ID" \ + --key "$NOTARIZATION_KEY_PATH" \ + --wait + +echo "Stapling notarization ticket..." +xcrun stapler staple "$DMG_PATH" + +echo "Verifying Gatekeeper compliance..." +spctl --assess --type open --context id=gkb-strict "$DMG_PATH" +``` + +--- + +## 4. Release Checklist + +1. [ ] Update version number in `MacSweep.xcodeproj` (`MARKETING_VERSION` and `CURRENT_PROJECT_VERSION`). +2. [ ] Update `CHANGELOG.md` with release notes and feature highlights. +3. [ ] Run full local unit test suite: `./scripts/test.sh`. +4. [ ] Verify SwiftLint compliance: `swiftlint lint`. +5. [ ] Create signed git tag: `git tag -a v1.0.0 -m "Release v1.0.0"`. +6. [ ] Push git tag to GitHub: `git push origin v1.0.0`. +7. [ ] Verify downloaded `.dmg` mounts properly and passes Gatekeeper (`spctl --assess`). diff --git a/docs/security.md b/docs/security.md new file mode 100644 index 0000000..84556ba --- /dev/null +++ b/docs/security.md @@ -0,0 +1,28 @@ +# MacSweep Security & Safety Specification +**Organization:** OpenCorex +**Project:** MacSweep (`OpenCorex/macsweep`) + +--- + +## Safety Architecture Guidelines + +### Protected Paths Policy +MacSweep hardcodes immutable protected system paths in `ProtectedPaths.swift`: +- `/System/` +- `/usr/` +- `/bin/` +- `/sbin/` +- `/var/db/` +- `/private/var/db/` +- `/Library/Apple/` +- `/Library/CoreServices/` +- `~/Library/Keychains/` +- `~/Library/Application Support/com.apple.*/` + +### Path Validation & Symlink Protection +1. **Symlink Resolution:** All paths are canonicalized via `URL.resolvingSymlinksInPath()` before evaluation. +2. **Path Traversal Guards:** Candidates attempting to escape the target directory or user sandbox boundaries are strictly rejected. +3. **No Shell Invocations:** Shell commands (`rm -rf`) are strictly prohibited. All deletions are executed via native `FileManager.default.trashItem(at:resultingItemURL:)` or verified native `FileManager` calls. + +### Disclosure & Vulnerability Reporting +Security vulnerabilities should be reported directly to OpenCorex security maintainers at `security@opencorex.org` or via GitHub Security Advisories. From 8fc4f0029c589a468b16cd5079629e79fcaf40f0 Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Mon, 24 Aug 2026 08:59:40 +0530 Subject: [PATCH 02/55] feat(core): implement core utility modules (logging, byte/date formatters, process runner) --- MacSweep/Core/Utilities/ByteFormatter.swift | 39 ++++++++++++++ MacSweep/Core/Utilities/DateFormatter.swift | 42 +++++++++++++++ MacSweep/Core/Utilities/Logger.swift | 28 ++++++++++ MacSweep/Core/Utilities/ProcessRunner.swift | 57 +++++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 MacSweep/Core/Utilities/ByteFormatter.swift create mode 100644 MacSweep/Core/Utilities/DateFormatter.swift create mode 100644 MacSweep/Core/Utilities/Logger.swift create mode 100644 MacSweep/Core/Utilities/ProcessRunner.swift diff --git a/MacSweep/Core/Utilities/ByteFormatter.swift b/MacSweep/Core/Utilities/ByteFormatter.swift new file mode 100644 index 0000000..16bf5d0 --- /dev/null +++ b/MacSweep/Core/Utilities/ByteFormatter.swift @@ -0,0 +1,39 @@ +import Foundation + +/// Formats raw byte counts into human-readable storage size strings (e.g., "14.2 GB"). +public struct ByteFormatter { + private static let formatter: ByteCountFormatter = { + let formatter = ByteCountFormatter() + formatter.countStyle = .file + formatter.allowedUnits = [.useBytes, .useKB, .useMB, .useGB, .useTB] + return formatter + }() + + /// Formats a byte count into a localized, human-readable string. + /// - Parameter bytes: The number of bytes to format. + /// - Returns: A formatted string such as "14.2 GB" or "512 MB". + public static func format(_ bytes: Int64) -> String { + return formatter.string(fromByteCount: bytes) + } + + /// Formats a byte count with explicit unit control. + /// - Parameters: + /// - bytes: The number of bytes to format. + /// - units: The allowed units to display. + /// - Returns: A formatted string in the specified units. + public static func format(_ bytes: Int64, units: ByteCountFormatter.Units) -> String { + let fmt = ByteCountFormatter() + fmt.countStyle = .file + fmt.allowedUnits = units + return fmt.string(fromByteCount: bytes) + } + + /// Returns a short compact representation (e.g., "14GB"). + public static func compact(_ bytes: Int64) -> String { + let fmt = ByteCountFormatter() + fmt.countStyle = .file + fmt.includesUnit = true + fmt.isAdaptive = true + return fmt.string(fromByteCount: bytes) + } +} diff --git a/MacSweep/Core/Utilities/DateFormatter.swift b/MacSweep/Core/Utilities/DateFormatter.swift new file mode 100644 index 0000000..5c57617 --- /dev/null +++ b/MacSweep/Core/Utilities/DateFormatter.swift @@ -0,0 +1,42 @@ +import Foundation + +/// Provides consistent date formatting across MacSweep for file metadata display. +public struct AppDateFormatter { + /// Displays relative dates like "2 hours ago", "Yesterday", "3 days ago". + private static let relativeFormatter: RelativeDateTimeFormatter = { + let formatter = RelativeDateTimeFormatter() + formatter.unitsStyle = .full + return formatter + }() + + /// Standard medium date format (e.g., "Aug 24, 2026"). + private static let mediumFormatter: Foundation.DateFormatter = { + let formatter = Foundation.DateFormatter() + formatter.dateStyle = .medium + formatter.timeStyle = .none + return formatter + }() + + /// Full date and time format (e.g., "Aug 24, 2026 at 8:10 AM"). + private static let fullFormatter: Foundation.DateFormatter = { + let formatter = Foundation.DateFormatter() + formatter.dateStyle = .medium + formatter.timeStyle = .short + return formatter + }() + + /// Formats a date as a relative string (e.g., "2 hours ago"). + public static func relative(_ date: Date) -> String { + return relativeFormatter.localizedString(for: date, relativeTo: Date()) + } + + /// Formats a date as a medium-style string (e.g., "Aug 24, 2026"). + public static func medium(_ date: Date) -> String { + return mediumFormatter.string(from: date) + } + + /// Formats a date with both date and time (e.g., "Aug 24, 2026 at 8:10 AM"). + public static func full(_ date: Date) -> String { + return fullFormatter.string(from: date) + } +} diff --git a/MacSweep/Core/Utilities/Logger.swift b/MacSweep/Core/Utilities/Logger.swift new file mode 100644 index 0000000..950387f --- /dev/null +++ b/MacSweep/Core/Utilities/Logger.swift @@ -0,0 +1,28 @@ +import OSLog + +/// Centralized logging facility for MacSweep using Apple's unified logging system. +/// All log categories are organized by subsystem module to enable targeted diagnostics. +public extension Logger { + private static let subsystem = "org.opencorex.macsweep" + + /// General application lifecycle events + static let app = Logger(subsystem: subsystem, category: "App") + + /// File scanning operations and discovery events + static let scanner = Logger(subsystem: subsystem, category: "Scanner") + + /// File deletion and trash relocation operations + static let cleaner = Logger(subsystem: subsystem, category: "Cleaner") + + /// Safety validation, protected path checks, and risk assessment + static let safety = Logger(subsystem: subsystem, category: "Safety") + + /// Permission checks, FDA status, and access scope management + static let permissions = Logger(subsystem: subsystem, category: "Permissions") + + /// Disk space metrics, storage analysis, and size calculations + static let storage = Logger(subsystem: subsystem, category: "Storage") + + /// File system enumeration and metadata operations + static let fileSystem = Logger(subsystem: subsystem, category: "FileSystem") +} diff --git a/MacSweep/Core/Utilities/ProcessRunner.swift b/MacSweep/Core/Utilities/ProcessRunner.swift new file mode 100644 index 0000000..cc98e42 --- /dev/null +++ b/MacSweep/Core/Utilities/ProcessRunner.swift @@ -0,0 +1,57 @@ +import Foundation +import OSLog + +/// Provides safe, controlled subprocess execution for limited external tool interaction. +/// MacSweep avoids shell execution wherever possible, preferring native APIs. +/// This runner is used only for Xcode/developer tool metadata queries. +public actor ProcessRunner { + /// Runs a process and returns its standard output as a trimmed string. + /// - Parameters: + /// - executableURL: The full path to the executable (e.g., `/usr/bin/xcode-select`). + /// - arguments: The arguments to pass to the executable. + /// - timeout: Maximum execution time in seconds before termination. + /// - Returns: The trimmed standard output string. + /// - Throws: `MacSweepError` if the process fails or times out. + public static func run( + executableURL: URL, + arguments: [String], + timeout: TimeInterval = 30 + ) async throws -> String { + Logger.app.debug("Running process: \(executableURL.path, privacy: .public) \(arguments.joined(separator: " "), privacy: .private)") + + let process = Process() + process.executableURL = executableURL + process.arguments = arguments + + let outputPipe = Pipe() + let errorPipe = Pipe() + process.standardOutput = outputPipe + process.standardError = errorPipe + + try process.run() + + // Enforce timeout + let timeoutTask = Task { + try await Task.sleep(for: .seconds(timeout)) + if process.isRunning { + process.terminate() + Logger.app.warning("Process timed out after \(timeout)s: \(executableURL.path, privacy: .public)") + } + } + + process.waitUntilExit() + timeoutTask.cancel() + + let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() + let output = String(data: outputData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" + + guard process.terminationStatus == 0 else { + let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile() + let errorOutput = String(data: errorData, encoding: .utf8) ?? "Unknown error" + Logger.app.error("Process failed with status \(process.terminationStatus): \(errorOutput, privacy: .private)") + throw MacSweepError.operationCancelled + } + + return output + } +} From 3b682fc1f0903765ba6d34496c83739c32857803 Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Mon, 24 Aug 2026 08:59:43 +0530 Subject: [PATCH 03/55] feat(core): implement safety validation layer and system boundary protections --- MacSweep/Core/Safety/PathValidator.swift | 41 ++++++++ .../Core/Safety/PermissionValidator.swift | 40 ++++++++ MacSweep/Core/Safety/ProtectedPaths.swift | 73 +++++++++++++++ MacSweep/Core/Safety/SafetyPolicy.swift | 33 +++++++ MacSweep/Core/Safety/SafetyValidator.swift | 93 +++++++++++++++++++ 5 files changed, 280 insertions(+) create mode 100644 MacSweep/Core/Safety/PathValidator.swift create mode 100644 MacSweep/Core/Safety/PermissionValidator.swift create mode 100644 MacSweep/Core/Safety/ProtectedPaths.swift create mode 100644 MacSweep/Core/Safety/SafetyPolicy.swift create mode 100644 MacSweep/Core/Safety/SafetyValidator.swift diff --git a/MacSweep/Core/Safety/PathValidator.swift b/MacSweep/Core/Safety/PathValidator.swift new file mode 100644 index 0000000..57a3739 --- /dev/null +++ b/MacSweep/Core/Safety/PathValidator.swift @@ -0,0 +1,41 @@ +import Foundation +import OSLog + +/// Validates and canonicalizes file paths to prevent symlink attacks and path traversal. +public struct PathValidator: Sendable { + /// Resolves the canonical path by following symlinks and verifying the result + /// stays within acceptable boundaries. + /// - Parameters: + /// - url: The URL to validate. + /// - allowedRoots: Set of allowed root paths the resolved path must reside under. + /// - Returns: The resolved canonical URL. + /// - Throws: `MacSweepError.symlinkEscape` if the resolved path escapes boundaries. + public static func resolveAndValidate(_ url: URL, allowedRoots: [URL]) throws -> URL { + let resolved = url.resolvingSymlinksInPath() + let resolvedPath = resolved.path + + // Check the resolved path is within an allowed root + let isAllowed = allowedRoots.contains { root in + resolvedPath.hasPrefix(root.path) + } + + guard isAllowed else { + Logger.safety.error("Symlink escape detected: \(url.path, privacy: .private) → \(resolvedPath, privacy: .private)") + throw MacSweepError.symlinkEscape(path: url.path, resolvedPath: resolvedPath) + } + + return resolved + } + + /// Checks whether a URL points to a symbolic link. + public static func isSymlink(_ url: URL) -> Bool { + let resourceValues = try? url.resourceValues(forKeys: [.isSymbolicLinkKey]) + return resourceValues?.isSymbolicLink ?? false + } + + /// Validates that a path does not contain path traversal components like ".." + public static func containsTraversal(_ path: String) -> Bool { + let components = (path as NSString).pathComponents + return components.contains("..") + } +} diff --git a/MacSweep/Core/Safety/PermissionValidator.swift b/MacSweep/Core/Safety/PermissionValidator.swift new file mode 100644 index 0000000..f9ec9f0 --- /dev/null +++ b/MacSweep/Core/Safety/PermissionValidator.swift @@ -0,0 +1,40 @@ +import Foundation +import OSLog + +/// Validates read/write permissions on file paths before scan or cleanup operations. +public struct PermissionValidator: Sendable { + private static let fileManager = FileManager.default + + /// Checks if a path is readable. + public static func isReadable(_ url: URL) -> Bool { + return fileManager.isReadableFile(atPath: url.path) + } + + /// Checks if a path is writable (required for deletion). + public static func isWritable(_ url: URL) -> Bool { + return fileManager.isWritableFile(atPath: url.path) + } + + /// Checks if a path is deletable. + public static func isDeletable(_ url: URL) -> Bool { + return fileManager.isDeletableFile(atPath: url.path) + } + + /// Checks if a file is locked by the system. + public static func isLocked(_ url: URL) -> Bool { + guard let resourceValues = try? url.resourceValues(forKeys: [.isUserImmutableKey, .isSystemImmutableKey]) else { + return false + } + return (resourceValues.isUserImmutable ?? false) || (resourceValues.isSystemImmutable ?? false) + } + + /// Comprehensive permission check for cleanup operations. + /// Returns a tuple indicating (canRead, canDelete, isLocked). + public static func checkPermissions(_ url: URL) -> (canRead: Bool, canDelete: Bool, isLocked: Bool) { + return ( + canRead: isReadable(url), + canDelete: isDeletable(url), + isLocked: isLocked(url) + ) + } +} diff --git a/MacSweep/Core/Safety/ProtectedPaths.swift b/MacSweep/Core/Safety/ProtectedPaths.swift new file mode 100644 index 0000000..70821e1 --- /dev/null +++ b/MacSweep/Core/Safety/ProtectedPaths.swift @@ -0,0 +1,73 @@ +import Foundation + +/// Immutable database of protected macOS system paths that must never be modified or deleted. +/// This is the foundation of MacSweep's safety architecture. +public struct ProtectedPaths: Sendable { + /// Absolute paths that are always protected. Any candidate within these directories is rejected. + public static let systemRoots: Set = [ + "/System", + "/usr", + "/bin", + "/sbin", + "/var/db", + "/private/var/db", + "/Library/Apple", + "/Library/CoreServices", + "/Library/Extensions", + "/Library/Filesystems", + "/Library/Frameworks", + "/private/var/protected" + ] + + /// User-domain paths that must be protected from deletion. + public static let userProtected: [String] = [ + "Library/Keychains", + "Library/Application Support/com.apple.TCC", + "Library/Application Support/com.apple.sharedfilelist", + "Library/Preferences/com.apple.loginwindow.plist", + "Library/Preferences/com.apple.dock.plist", + "Library/Preferences/com.apple.finder.plist", + "Library/Preferences/com.apple.systempreferences.plist", + ".ssh", + ".gnupg", + ".CFUserTextEncoding" + ] + + /// File extensions that should never be cleaned without explicit high-risk confirmation. + public static let sensitiveExtensions: Set = [ + "keychain", + "keychain-db", + "pem", + "p12", + "cer", + "key", + "ppk" + ] + + /// Returns true if the given absolute path falls within a protected system root. + public static func isSystemProtected(_ path: String) -> Bool { + for root in systemRoots { + if path == root || path.hasPrefix(root + "/") { + return true + } + } + return false + } + + /// Returns true if the given path is a protected user-domain path. + public static func isUserProtected(_ path: String, homeDirectory: URL) -> Bool { + let homePath = homeDirectory.path + for protectedRelative in userProtected { + let fullProtected = (homePath as NSString).appendingPathComponent(protectedRelative) + if path == fullProtected || path.hasPrefix(fullProtected + "/") { + return true + } + } + return false + } + + /// Returns true if the file extension indicates a sensitive credential file. + public static func hasSensitiveExtension(_ url: URL) -> Bool { + return sensitiveExtensions.contains(url.pathExtension.lowercased()) + } +} diff --git a/MacSweep/Core/Safety/SafetyPolicy.swift b/MacSweep/Core/Safety/SafetyPolicy.swift new file mode 100644 index 0000000..097bfd5 --- /dev/null +++ b/MacSweep/Core/Safety/SafetyPolicy.swift @@ -0,0 +1,33 @@ +import Foundation + +/// Defines the safety policy rules applied to candidate cleanup items. +public struct SafetyPolicy: Sendable { + /// Minimum file age in days before items are eligible for safe cleanup. + public let minimumAgeDays: Int + + /// Whether to skip items currently in use (locked files). + public let skipLockedFiles: Bool + + /// Whether to resolve and verify symlinks before operations. + public let verifySymlinks: Bool + + /// Default production safety policy. + public static let `default` = SafetyPolicy( + minimumAgeDays: 0, + skipLockedFiles: true, + verifySymlinks: true + ) + + /// Strict policy for high-risk operations. + public static let strict = SafetyPolicy( + minimumAgeDays: 7, + skipLockedFiles: true, + verifySymlinks: true + ) + + public init(minimumAgeDays: Int, skipLockedFiles: Bool, verifySymlinks: Bool) { + self.minimumAgeDays = minimumAgeDays + self.skipLockedFiles = skipLockedFiles + self.verifySymlinks = verifySymlinks + } +} diff --git a/MacSweep/Core/Safety/SafetyValidator.swift b/MacSweep/Core/Safety/SafetyValidator.swift new file mode 100644 index 0000000..81cc6d4 --- /dev/null +++ b/MacSweep/Core/Safety/SafetyValidator.swift @@ -0,0 +1,93 @@ +import Foundation +import OSLog + +/// Central safety validation engine that enforces all safety rules before cleanup. +/// Every candidate item must pass through this validator before being queued for deletion. +public final class SafetyValidator: Sendable { + private let policy: SafetyPolicy + private let homeDirectory: URL + + public init(policy: SafetyPolicy = .default) { + self.policy = policy + self.homeDirectory = FileManager.default.homeDirectoryForCurrentUser + } + + /// Validates whether a URL is safe for cleanup operations. + /// - Parameter url: The candidate file or directory URL. + /// - Returns: A `ValidationResult` indicating pass/fail with a reason. + public func validate(_ url: URL) -> ValidationResult { + let path = url.path + + // Step 1: Check for path traversal components + if PathValidator.containsTraversal(path) { + Logger.safety.warning("Path traversal rejected: \(path, privacy: .private)") + return .rejected(reason: "Path contains traversal components") + } + + // Step 2: Check against protected system roots + if ProtectedPaths.isSystemProtected(path) { + Logger.safety.warning("Protected system path rejected: \(path, privacy: .private)") + return .rejected(reason: "Protected system directory") + } + + // Step 3: Check against protected user paths + if ProtectedPaths.isUserProtected(path, homeDirectory: homeDirectory) { + Logger.safety.warning("Protected user path rejected: \(path, privacy: .private)") + return .rejected(reason: "Protected user configuration") + } + + // Step 4: Check for sensitive file extensions + if ProtectedPaths.hasSensitiveExtension(url) { + Logger.safety.warning("Sensitive file extension rejected: \(path, privacy: .private)") + return .rejected(reason: "Sensitive credential file type") + } + + // Step 5: Resolve symlinks and verify boundaries + if policy.verifySymlinks && PathValidator.isSymlink(url) { + let resolved = url.resolvingSymlinksInPath() + let resolvedPath = resolved.path + + if ProtectedPaths.isSystemProtected(resolvedPath) { + Logger.safety.error("Symlink escape to system path: \(path, privacy: .private) → \(resolvedPath, privacy: .private)") + return .rejected(reason: "Symlink points to protected system path") + } + } + + // Step 6: Check if file is locked + if policy.skipLockedFiles && PermissionValidator.isLocked(url) { + Logger.safety.info("Locked file skipped: \(path, privacy: .private)") + return .skipped(reason: "File is locked by the system") + } + + // Step 7: Verify deletion permissions + if !PermissionValidator.isDeletable(url) { + return .skipped(reason: "Insufficient permissions to delete") + } + + return .approved + } + + /// Batch validates an array of URLs and returns only approved items. + public func filterApproved(_ urls: [URL]) -> [URL] { + return urls.filter { validate($0) == .approved } + } +} + +/// Result of a safety validation check. +public enum ValidationResult: Equatable, Sendable { + /// Item passed all safety checks and is approved for user review. + case approved + /// Item was explicitly rejected due to safety violation. + case rejected(reason: String) + /// Item was skipped due to permission or state issues (not a safety violation). + case skipped(reason: String) + + public static func == (lhs: ValidationResult, rhs: ValidationResult) -> Bool { + switch (lhs, rhs) { + case (.approved, .approved): return true + case (.rejected(let a), .rejected(let b)): return a == b + case (.skipped(let a), .skipped(let b)): return a == b + default: return false + } + } +} From 4c825c36ee897b3d102a3c18b7b2b80172ec3d2b Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Mon, 24 Aug 2026 08:59:48 +0530 Subject: [PATCH 04/55] feat(core): implement permission management and Full Disk Access detection --- .../Permissions/FullDiskAccessManager.swift | 27 +++++++++++ .../Core/Permissions/PermissionManager.swift | 45 +++++++++++++++++++ .../SecurityScopedBookmarkManager.swift | 45 +++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 MacSweep/Core/Permissions/FullDiskAccessManager.swift create mode 100644 MacSweep/Core/Permissions/PermissionManager.swift create mode 100644 MacSweep/Core/Permissions/SecurityScopedBookmarkManager.swift diff --git a/MacSweep/Core/Permissions/FullDiskAccessManager.swift b/MacSweep/Core/Permissions/FullDiskAccessManager.swift new file mode 100644 index 0000000..5831c4a --- /dev/null +++ b/MacSweep/Core/Permissions/FullDiskAccessManager.swift @@ -0,0 +1,27 @@ +import Foundation +import AppKit +import OSLog + +/// Manages Full Disk Access detection and guides users through macOS System Settings. +public final class FullDiskAccessManager: Sendable { + public static let shared = FullDiskAccessManager() + + /// System path used to test Full Disk Access grant. + private let testPath = "/Library/Preferences/com.apple.TimeMachine.plist" + + /// Returns `true` if Full Disk Access is currently granted. + public var hasFullDiskAccess: Bool { + let result = FileManager.default.isReadableFile(atPath: testPath) + Logger.permissions.debug("Full Disk Access check: \(result ? "granted" : "not granted")") + return result + } + + /// Opens macOS System Settings directly to Privacy & Security > Full Disk Access. + @MainActor + public func openSystemSettings() { + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") { + NSWorkspace.shared.open(url) + Logger.permissions.info("Opened System Settings for Full Disk Access") + } + } +} diff --git a/MacSweep/Core/Permissions/PermissionManager.swift b/MacSweep/Core/Permissions/PermissionManager.swift new file mode 100644 index 0000000..5be3e9d --- /dev/null +++ b/MacSweep/Core/Permissions/PermissionManager.swift @@ -0,0 +1,45 @@ +import Foundation +import OSLog +import Combine + +/// Centralized permission state manager that tracks Full Disk Access +/// and other permission grants across the application lifecycle. +@MainActor +public final class PermissionManager: ObservableObject { + @Published public var hasFullDiskAccess: Bool = false + + private var notificationObserver: NSObjectProtocol? + + public init() { + refreshPermissions() + observeAppActivation() + } + + deinit { + if let observer = notificationObserver { + NotificationCenter.default.removeObserver(observer) + } + } + + /// Refreshes all permission states. + public func refreshPermissions() { + hasFullDiskAccess = FullDiskAccessManager.shared.hasFullDiskAccess + Logger.permissions.info("Permissions refreshed — FDA: \(self.hasFullDiskAccess)") + } + + /// Opens System Settings to the Full Disk Access pane. + public func requestFullDiskAccess() { + FullDiskAccessManager.shared.openSystemSettings() + } + + /// Observes app activation events to re-check permissions dynamically. + private func observeAppActivation() { + notificationObserver = NotificationCenter.default.addObserver( + forName: NSApplication.didBecomeActiveNotification, + object: nil, + queue: .main + ) { [weak self] _ in + self?.refreshPermissions() + } + } +} diff --git a/MacSweep/Core/Permissions/SecurityScopedBookmarkManager.swift b/MacSweep/Core/Permissions/SecurityScopedBookmarkManager.swift new file mode 100644 index 0000000..a1cba99 --- /dev/null +++ b/MacSweep/Core/Permissions/SecurityScopedBookmarkManager.swift @@ -0,0 +1,45 @@ +import Foundation +import OSLog + +/// Manages security-scoped bookmark persistence for user-selected directories. +/// Reserved for future use if App Store distribution is pursued. +public final class SecurityScopedBookmarkManager { + private let storageKey = "MacSweep_SecurityScopedBookmarks" + + public init() {} + + /// Saves a security-scoped bookmark for a user-selected URL. + public func saveBookmark(for url: URL) throws { + let bookmarkData = try url.bookmarkData( + options: .withSecurityScope, + includingResourceValuesForKeys: nil, + relativeTo: nil + ) + var bookmarks = UserDefaults.standard.dictionary(forKey: storageKey) as? [String: Data] ?? [:] + bookmarks[url.path] = bookmarkData + UserDefaults.standard.set(bookmarks, forKey: storageKey) + Logger.permissions.info("Saved bookmark for: \(url.path, privacy: .private)") + } + + /// Resolves a previously saved bookmark and provides scoped access. + public func resolveBookmark(for path: String) throws -> URL { + guard let bookmarks = UserDefaults.standard.dictionary(forKey: storageKey) as? [String: Data], + let data = bookmarks[path] else { + throw MacSweepError.permissionDenied(path: path) + } + + var isStale = false + let url = try URL( + resolvingBookmarkData: data, + options: .withSecurityScope, + relativeTo: nil, + bookmarkDataIsStale: &isStale + ) + + if isStale { + try saveBookmark(for: url) + } + + return url + } +} From 468c074dda38bf6a74a17783428aa2babda759ce Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Mon, 24 Aug 2026 08:59:50 +0530 Subject: [PATCH 05/55] feat(core): implement file system enumeration, metadata, and access management --- .../Core/FileSystem/FileAccessManager.swift | 41 ++++++++++ MacSweep/Core/FileSystem/FileEnumerator.swift | 77 +++++++++++++++++++ MacSweep/Core/FileSystem/FileMetadata.swift | 40 ++++++++++ .../Core/FileSystem/FileSystemService.swift | 49 ++++++++++++ 4 files changed, 207 insertions(+) create mode 100644 MacSweep/Core/FileSystem/FileAccessManager.swift create mode 100644 MacSweep/Core/FileSystem/FileEnumerator.swift create mode 100644 MacSweep/Core/FileSystem/FileMetadata.swift create mode 100644 MacSweep/Core/FileSystem/FileSystemService.swift diff --git a/MacSweep/Core/FileSystem/FileAccessManager.swift b/MacSweep/Core/FileSystem/FileAccessManager.swift new file mode 100644 index 0000000..b9dea06 --- /dev/null +++ b/MacSweep/Core/FileSystem/FileAccessManager.swift @@ -0,0 +1,41 @@ +import Foundation +import OSLog + +/// Manages controlled file access for scan and cleanup operations. +public final class FileAccessManager: Sendable { + public static let shared = FileAccessManager() + + private let fileManager = FileManager.default + + /// Returns the user's home directory. + public var homeDirectory: URL { + fileManager.homeDirectoryForCurrentUser + } + + /// Returns the user's Library directory. + public var libraryDirectory: URL { + homeDirectory.appendingPathComponent("Library") + } + + /// Returns the user's Caches directory. + public var cachesDirectory: URL { + libraryDirectory.appendingPathComponent("Caches") + } + + /// Returns the user's Logs directory. + public var logsDirectory: URL { + libraryDirectory.appendingPathComponent("Logs") + } + + /// Returns commonly scannable root directories. + public var scannableRoots: [URL] { + [ + homeDirectory, + homeDirectory.appendingPathComponent("Downloads"), + homeDirectory.appendingPathComponent("Documents"), + homeDirectory.appendingPathComponent("Desktop"), + homeDirectory.appendingPathComponent("Movies"), + homeDirectory.appendingPathComponent("Music") + ] + } +} diff --git a/MacSweep/Core/FileSystem/FileEnumerator.swift b/MacSweep/Core/FileSystem/FileEnumerator.swift new file mode 100644 index 0000000..4251653 --- /dev/null +++ b/MacSweep/Core/FileSystem/FileEnumerator.swift @@ -0,0 +1,77 @@ +import Foundation +import OSLog + +/// Provides async file system enumeration using FileManager.DirectoryEnumerator. +/// Gracefully skips permission-denied and inaccessible paths. +public struct FileEnumerator: Sendable { + /// Enumerates all files within a directory, yielding metadata for each file found. + /// - Parameters: + /// - directoryURL: The root directory to enumerate. + /// - includeHidden: Whether to include hidden files. + /// - skipPackageDescendants: Whether to skip inside app bundles/packages. + /// - Returns: An async sequence of `FileMetadata` objects. + public static func enumerate( + directory directoryURL: URL, + includeHidden: Bool = false, + skipPackageDescendants: Bool = true + ) -> AsyncStream { + AsyncStream { continuation in + Task.detached(priority: .userInitiated) { + let fileManager = FileManager.default + + var options: FileManager.DirectoryEnumerationOptions = [.producesRelativePathURLs] + if !includeHidden { + options.insert(.skipsHiddenFiles) + } + if skipPackageDescendants { + options.insert(.skipsPackageDescendants) + } + + guard let enumerator = fileManager.enumerator( + at: directoryURL, + includingPropertiesForKeys: [ + .fileSizeKey, + .totalFileAllocatedSizeKey, + .isDirectoryKey, + .isSymbolicLinkKey, + .contentModificationDateKey, + .isHiddenKey + ], + options: options, + errorHandler: { url, error in + Logger.fileSystem.debug("Enumeration skipped \(url.path, privacy: .private): \(error.localizedDescription)") + return true // Continue enumeration + } + ) else { + Logger.fileSystem.warning("Failed to create enumerator for: \(directoryURL.path, privacy: .private)") + continuation.finish() + return + } + + for case let fileURL as URL in enumerator { + if Task.isCancelled { + break + } + if let metadata = FileMetadata.from(fileURL) { + continuation.yield(metadata) + } + } + + continuation.finish() + } + } + } + + /// Calculates the total size of a directory by summing all contained file sizes. + public static func directorySize(_ url: URL) async -> Int64 { + var totalSize: Int64 = 0 + + for await metadata in enumerate(directory: url, includeHidden: true) { + if !metadata.isDirectory { + totalSize += metadata.size + } + } + + return totalSize + } +} diff --git a/MacSweep/Core/FileSystem/FileMetadata.swift b/MacSweep/Core/FileSystem/FileMetadata.swift new file mode 100644 index 0000000..f1f43fa --- /dev/null +++ b/MacSweep/Core/FileSystem/FileMetadata.swift @@ -0,0 +1,40 @@ +import Foundation + +/// Extracts and holds metadata for a single file or directory from the file system. +public struct FileMetadata: Sendable { + public let url: URL + public let size: Int64 + public let isDirectory: Bool + public let isSymbolicLink: Bool + public let modificationDate: Date + public let isHidden: Bool + + /// Creates metadata by reading resource values from the file system. + /// - Parameter url: The file URL to inspect. + /// - Returns: A populated `FileMetadata`, or nil if the file is unreadable. + public static func from(_ url: URL) -> FileMetadata? { + let keys: Set = [ + .fileSizeKey, + .totalFileAllocatedSizeKey, + .isDirectoryKey, + .isSymbolicLinkKey, + .contentModificationDateKey, + .isHiddenKey + ] + + guard let values = try? url.resourceValues(forKeys: keys) else { + return nil + } + + let size: Int64 = Int64(values.totalFileAllocatedSize ?? values.fileSize ?? 0) + + return FileMetadata( + url: url, + size: size, + isDirectory: values.isDirectory ?? false, + isSymbolicLink: values.isSymbolicLink ?? false, + modificationDate: values.contentModificationDate ?? Date.distantPast, + isHidden: values.isHidden ?? false + ) + } +} diff --git a/MacSweep/Core/FileSystem/FileSystemService.swift b/MacSweep/Core/FileSystem/FileSystemService.swift new file mode 100644 index 0000000..d22dc82 --- /dev/null +++ b/MacSweep/Core/FileSystem/FileSystemService.swift @@ -0,0 +1,49 @@ +import Foundation +import OSLog + +/// High-level file system operations wrapper used across MacSweep. +public final class FileSystemService: Sendable { + private let fileManager = FileManager.default + + public init() {} + + /// Checks if a directory exists at the given path. + public func directoryExists(at url: URL) -> Bool { + var isDirectory: ObjCBool = false + let exists = fileManager.fileExists(atPath: url.path, isDirectory: &isDirectory) + return exists && isDirectory.boolValue + } + + /// Checks if a file exists at the given path. + public func fileExists(at url: URL) -> Bool { + return fileManager.fileExists(atPath: url.path) + } + + /// Returns the contents (child URLs) of a directory. + public func contentsOfDirectory(at url: URL) throws -> [URL] { + return try fileManager.contentsOfDirectory( + at: url, + includingPropertiesForKeys: [.fileSizeKey, .isDirectoryKey, .contentModificationDateKey], + options: [.skipsHiddenFiles] + ) + } + + /// Returns the total allocated size of a single file. + public func fileSize(at url: URL) -> Int64 { + guard let values = try? url.resourceValues(forKeys: [.totalFileAllocatedSizeKey, .fileSizeKey]) else { + return 0 + } + return Int64(values.totalFileAllocatedSize ?? values.fileSize ?? 0) + } + + /// Calculates the total size of a directory recursively. + public func directorySize(at url: URL) async -> Int64 { + return await FileEnumerator.directorySize(url) + } + + /// Reveals a file or directory in Finder. + @MainActor + public func revealInFinder(_ url: URL) { + NSWorkspace.shared.activateFileViewerSelecting([url]) + } +} From 1118485cd646a38d399d93a19c7d5d0de3cd009a Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Mon, 24 Aug 2026 08:59:54 +0530 Subject: [PATCH 06/55] feat(core): implement storage analysis, disk metrics, and directory size calculation --- .../Storage/DirectorySizeCalculator.swift | 74 +++++++++++++++++++ MacSweep/Core/Storage/DiskSpaceService.swift | 57 ++++++++++++++ MacSweep/Core/Storage/FileSizeFormatter.swift | 22 ++++++ MacSweep/Core/Storage/StorageAnalyzer.swift | 15 ++++ 4 files changed, 168 insertions(+) create mode 100644 MacSweep/Core/Storage/DirectorySizeCalculator.swift create mode 100644 MacSweep/Core/Storage/DiskSpaceService.swift create mode 100644 MacSweep/Core/Storage/FileSizeFormatter.swift create mode 100644 MacSweep/Core/Storage/StorageAnalyzer.swift diff --git a/MacSweep/Core/Storage/DirectorySizeCalculator.swift b/MacSweep/Core/Storage/DirectorySizeCalculator.swift new file mode 100644 index 0000000..acae1ee --- /dev/null +++ b/MacSweep/Core/Storage/DirectorySizeCalculator.swift @@ -0,0 +1,74 @@ +import Foundation +import OSLog + +/// Calculates directory sizes efficiently for the Storage Analyzer. +public actor DirectorySizeCalculator { + /// Calculates the size of a directory, yielding child node sizes. + /// - Parameter url: The root directory URL. + /// - Returns: A `StorageNode` tree with calculated sizes. + public func calculateTree(at url: URL, maxDepth: Int = 3) async -> StorageNode { + let size = await calculateDirectorySize(url) + var children: [StorageNode] = [] + + if maxDepth > 0 { + let fileManager = FileManager.default + if let contents = try? fileManager.contentsOfDirectory( + at: url, + includingPropertiesForKeys: [.isDirectoryKey, .fileSizeKey, .totalFileAllocatedSizeKey], + options: [.skipsHiddenFiles] + ) { + for childURL in contents { + if Task.isCancelled { break } + + let values = try? childURL.resourceValues(forKeys: [.isDirectoryKey]) + let isDir = values?.isDirectory ?? false + + if isDir { + let childNode = await calculateTree(at: childURL, maxDepth: maxDepth - 1) + children.append(childNode) + } else { + let fileSize = Self.fileSize(at: childURL) + let node = StorageNode( + url: childURL, + isDirectory: false, + size: fileSize + ) + children.append(node) + } + } + } + } + + // Sort children by size descending + children.sort { $0.size > $1.size } + + // Calculate percentage of parent + if size > 0 { + children = children.map { child in + var mutableChild = child + mutableChild.percentageOfParent = Double(child.size) / Double(size) + return mutableChild + } + } + + return StorageNode( + url: url, + isDirectory: true, + size: size, + children: children + ) + } + + /// Calculates the total size of a directory by enumerating all files. + private func calculateDirectorySize(_ url: URL) async -> Int64 { + return await FileEnumerator.directorySize(url) + } + + /// Returns the allocated size of a single file. + private static func fileSize(at url: URL) -> Int64 { + guard let values = try? url.resourceValues(forKeys: [.totalFileAllocatedSizeKey, .fileSizeKey]) else { + return 0 + } + return Int64(values.totalFileAllocatedSize ?? values.fileSize ?? 0) + } +} diff --git a/MacSweep/Core/Storage/DiskSpaceService.swift b/MacSweep/Core/Storage/DiskSpaceService.swift new file mode 100644 index 0000000..79e6045 --- /dev/null +++ b/MacSweep/Core/Storage/DiskSpaceService.swift @@ -0,0 +1,57 @@ +import Foundation +import OSLog + +/// Provides real-time disk space metrics for the boot volume. +public final class DiskSpaceService: Sendable { + public init() {} + + /// Retrieves current disk space information for the boot volume. + public func getDiskSpace() throws -> DiskSpace { + let homeURL = FileManager.default.homeDirectoryForCurrentUser + let values = try homeURL.resourceValues(forKeys: [ + .volumeTotalCapacityKey, + .volumeAvailableCapacityForImportantUsageKey, + .volumeAvailableCapacityKey, + .volumeNameKey + ]) + + guard let totalCapacity = values.volumeTotalCapacity, + let availableCapacity = values.volumeAvailableCapacityForImportantUsage else { + throw MacSweepError.diskSpaceUnavailable + } + + let total = Int64(totalCapacity) + let available = Int64(availableCapacity) + let used = total - available + + Logger.storage.debug("Disk space: \(ByteFormatter.format(used)) used of \(ByteFormatter.format(total))") + + return DiskSpace( + volumeName: values.volumeName ?? "Macintosh HD", + totalCapacity: total, + availableCapacity: available, + usedCapacity: used + ) + } +} + +/// Immutable snapshot of disk space information. +public struct DiskSpace: Sendable { + public let volumeName: String + public let totalCapacity: Int64 + public let availableCapacity: Int64 + public let usedCapacity: Int64 + + /// Percentage of disk used (0.0 to 1.0). + public var usedPercentage: Double { + guard totalCapacity > 0 else { return 0 } + return Double(usedCapacity) / Double(totalCapacity) + } + + /// Formatted total capacity string. + public var formattedTotal: String { ByteFormatter.format(totalCapacity) } + /// Formatted available capacity string. + public var formattedAvailable: String { ByteFormatter.format(availableCapacity) } + /// Formatted used capacity string. + public var formattedUsed: String { ByteFormatter.format(usedCapacity) } +} diff --git a/MacSweep/Core/Storage/FileSizeFormatter.swift b/MacSweep/Core/Storage/FileSizeFormatter.swift new file mode 100644 index 0000000..c070ab7 --- /dev/null +++ b/MacSweep/Core/Storage/FileSizeFormatter.swift @@ -0,0 +1,22 @@ +import Foundation + +/// Specialized size formatting utilities extending ByteFormatter for specific UI contexts. +public struct FileSizeFormatter { + /// Formats a size with a percentage relative to a total. + public static func formatWithPercentage(size: Int64, total: Int64) -> String { + let sizeStr = ByteFormatter.format(size) + guard total > 0 else { return sizeStr } + let percentage = Double(size) / Double(total) * 100 + return "\(sizeStr) (\(String(format: "%.1f", percentage))%)" + } + + /// Returns a short human-readable description of the size (e.g., "Small", "Medium", "Large"). + public static func sizeCategory(_ bytes: Int64) -> String { + switch bytes { + case 0..<1_048_576: return "Small" + case 1_048_576..<104_857_600: return "Medium" + case 104_857_600..<1_073_741_824: return "Large" + default: return "Very Large" + } + } +} diff --git a/MacSweep/Core/Storage/StorageAnalyzer.swift b/MacSweep/Core/Storage/StorageAnalyzer.swift new file mode 100644 index 0000000..d2363af --- /dev/null +++ b/MacSweep/Core/Storage/StorageAnalyzer.swift @@ -0,0 +1,15 @@ +import Foundation +import OSLog + +/// Coordinates storage analysis operations for the Storage Analyzer feature. +public actor StorageAnalyzer { + private let calculator = DirectorySizeCalculator() + + /// Analyzes storage usage for a given root directory. + public func analyze(rootURL: URL, maxDepth: Int = 3) async -> StorageNode { + Logger.storage.info("Starting storage analysis at: \(rootURL.path, privacy: .private)") + let tree = await calculator.calculateTree(at: rootURL, maxDepth: maxDepth) + Logger.storage.info("Storage analysis complete: \(ByteFormatter.format(tree.size))") + return tree + } +} From 0ffe870a6892222a1a2ef66ca1c5c80e806e2138 Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Mon, 24 Aug 2026 08:59:57 +0530 Subject: [PATCH 07/55] feat(core): implement scanning engine, coordinators, models, and specialized cache scanners --- MacSweep/Core/Scanner/ScanCategory.swift | 14 +++ MacSweep/Core/Scanner/ScanCoordinator.swift | 51 ++++++++ MacSweep/Core/Scanner/ScanEngine.swift | 113 ++++++++++++++++++ MacSweep/Core/Scanner/ScanItem.swift | 31 +++++ MacSweep/Core/Scanner/ScanProgress.swift | 38 ++++++ MacSweep/Core/Scanner/ScanResult.swift | 44 +++++++ .../Core/Scanner/Scanners/CacheScanner.swift | 67 +++++++++++ .../Core/Scanner/Scanners/DockerScanner.swift | 40 +++++++ .../Core/Scanner/Scanners/GradleScanner.swift | 42 +++++++ .../Scanner/Scanners/HomebrewScanner.swift | 40 +++++++ .../Core/Scanner/Scanners/LogScanner.swift | 42 +++++++ .../Core/Scanner/Scanners/NodeScanner.swift | 43 +++++++ .../Core/Scanner/Scanners/TrashScanner.swift | 58 +++++++++ .../Core/Scanner/Scanners/XcodeScanner.swift | 44 +++++++ 14 files changed, 667 insertions(+) create mode 100644 MacSweep/Core/Scanner/ScanCategory.swift create mode 100644 MacSweep/Core/Scanner/ScanCoordinator.swift create mode 100644 MacSweep/Core/Scanner/ScanEngine.swift create mode 100644 MacSweep/Core/Scanner/ScanItem.swift create mode 100644 MacSweep/Core/Scanner/ScanProgress.swift create mode 100644 MacSweep/Core/Scanner/ScanResult.swift create mode 100644 MacSweep/Core/Scanner/Scanners/CacheScanner.swift create mode 100644 MacSweep/Core/Scanner/Scanners/DockerScanner.swift create mode 100644 MacSweep/Core/Scanner/Scanners/GradleScanner.swift create mode 100644 MacSweep/Core/Scanner/Scanners/HomebrewScanner.swift create mode 100644 MacSweep/Core/Scanner/Scanners/LogScanner.swift create mode 100644 MacSweep/Core/Scanner/Scanners/NodeScanner.swift create mode 100644 MacSweep/Core/Scanner/Scanners/TrashScanner.swift create mode 100644 MacSweep/Core/Scanner/Scanners/XcodeScanner.swift diff --git a/MacSweep/Core/Scanner/ScanCategory.swift b/MacSweep/Core/Scanner/ScanCategory.swift new file mode 100644 index 0000000..8e3bfa3 --- /dev/null +++ b/MacSweep/Core/Scanner/ScanCategory.swift @@ -0,0 +1,14 @@ +import Foundation + +/// Configuration for a scannable category defining target paths and matching rules. +public struct ScanCategoryConfig: Sendable { + public let category: CleanupCategory + public let targetPaths: [URL] + public let recursive: Bool + + public init(category: CleanupCategory, targetPaths: [URL], recursive: Bool = true) { + self.category = category + self.targetPaths = targetPaths + self.recursive = recursive + } +} diff --git a/MacSweep/Core/Scanner/ScanCoordinator.swift b/MacSweep/Core/Scanner/ScanCoordinator.swift new file mode 100644 index 0000000..1431391 --- /dev/null +++ b/MacSweep/Core/Scanner/ScanCoordinator.swift @@ -0,0 +1,51 @@ +import Foundation +import OSLog + +/// Coordinates the lifecycle of scan operations, managing cancellation and state. +public actor ScanCoordinator { + private var currentScanTask: Task? + private let scanEngine: ScanEngine + + public var isScanning: Bool { + currentScanTask != nil && currentScanTask?.isCancelled == false + } + + public init(scanEngine: ScanEngine) { + self.scanEngine = scanEngine + } + + /// Starts a Smart Scan, cancelling any in-progress scan first. + public func startSmartScan( + progressHandler: @Sendable @escaping (ScanProgress) -> Void + ) async -> ScanResult { + cancelCurrentScan() + + let task = Task { + await scanEngine.performSmartScan(progressHandler: progressHandler) + } + currentScanTask = task + let result = await task.value + currentScanTask = nil + return result + } + + /// Starts a Developer-only scan. + public func startDeveloperScan() async -> ScanResult { + cancelCurrentScan() + + let task = Task { + await scanEngine.performDeveloperScan() + } + currentScanTask = task + let result = await task.value + currentScanTask = nil + return result + } + + /// Cancels any currently running scan. + public func cancelCurrentScan() { + currentScanTask?.cancel() + currentScanTask = nil + Logger.scanner.info("Scan cancelled") + } +} diff --git a/MacSweep/Core/Scanner/ScanEngine.swift b/MacSweep/Core/Scanner/ScanEngine.swift new file mode 100644 index 0000000..97bb454 --- /dev/null +++ b/MacSweep/Core/Scanner/ScanEngine.swift @@ -0,0 +1,113 @@ +import Foundation +import OSLog + +/// Central scan engine that orchestrates concurrent scanner execution, +/// safety validation, and result aggregation. +public actor ScanEngine { + private let safetyValidator: SafetyValidator + private let cacheScanner = CacheScanner() + private let logScanner = LogScanner() + private let trashScanner = TrashScanner() + private let xcodeScanner = XcodeScanner() + private let gradleScanner = GradleScanner() + private let nodeScanner = NodeScanner() + private let homebrewScanner = HomebrewScanner() + private let dockerScanner = DockerScanner() + + public init(safetyValidator: SafetyValidator) { + self.safetyValidator = safetyValidator + } + + /// Runs a full Smart Scan across all categories concurrently. + /// - Parameter progressHandler: Called with progress updates during scanning. + /// - Returns: A complete `ScanResult` with safety-validated items. + public func performSmartScan( + progressHandler: @Sendable (ScanProgress) -> Void = { _ in } + ) async -> ScanResult { + let startTime = Date() + Logger.scanner.info("Starting Smart Scan") + + var allItems: [CleanupItem] = [] + + // Run all scanners concurrently using a TaskGroup + await withTaskGroup(of: [ScanItem].self) { group in + group.addTask { await self.cacheScanner.scan() } + group.addTask { await self.logScanner.scan() } + group.addTask { await self.trashScanner.scan() } + group.addTask { await self.xcodeScanner.scan() } + group.addTask { await self.gradleScanner.scan() } + group.addTask { await self.nodeScanner.scan() } + group.addTask { await self.homebrewScanner.scan() } + group.addTask { await self.dockerScanner.scan() } + + for await scanItems in group { + // Validate each item through safety pipeline + for item in scanItems { + let result = safetyValidator.validate(item.url) + switch result { + case .approved: + allItems.append(item.toCleanupItem()) + case .rejected(let reason): + Logger.safety.debug("Rejected: \(item.url.path, privacy: .private) — \(reason)") + case .skipped(let reason): + Logger.safety.debug("Skipped: \(item.url.path, privacy: .private) — \(reason)") + } + } + + // Update progress + let totalBytes = allItems.reduce(0) { $0 + $1.size } + progressHandler(ScanProgress( + scannedBytes: totalBytes, + scannedItemsCount: allItems.count + )) + } + } + + // Sort items by size descending + allItems.sort { $0.size > $1.size } + + let duration = Date().timeIntervalSince(startTime) + let totalBytes = allItems.reduce(0) { $0 + $1.size } + + Logger.scanner.info("Smart Scan complete: \(allItems.count) items, \(ByteFormatter.format(totalBytes)), \(String(format: "%.1f", duration))s") + + progressHandler(ScanProgress.completed(totalBytes: totalBytes, totalItems: allItems.count)) + + return ScanResult( + items: allItems, + duration: duration, + scannedCategories: Array(Set(allItems.map(\.category))) + ) + } + + /// Runs a developer-only scan targeting dev tool caches. + public func performDeveloperScan() async -> ScanResult { + let startTime = Date() + var allItems: [CleanupItem] = [] + + await withTaskGroup(of: [ScanItem].self) { group in + group.addTask { await self.xcodeScanner.scan() } + group.addTask { await self.gradleScanner.scan() } + group.addTask { await self.nodeScanner.scan() } + group.addTask { await self.homebrewScanner.scan() } + group.addTask { await self.dockerScanner.scan() } + + for await scanItems in group { + for item in scanItems { + if safetyValidator.validate(item.url) == .approved { + allItems.append(item.toCleanupItem()) + } + } + } + } + + allItems.sort { $0.size > $1.size } + let duration = Date().timeIntervalSince(startTime) + + return ScanResult( + items: allItems, + duration: duration, + scannedCategories: Array(Set(allItems.map(\.category))) + ) + } +} diff --git a/MacSweep/Core/Scanner/ScanItem.swift b/MacSweep/Core/Scanner/ScanItem.swift new file mode 100644 index 0000000..6d2b11d --- /dev/null +++ b/MacSweep/Core/Scanner/ScanItem.swift @@ -0,0 +1,31 @@ +import Foundation + +/// Raw item discovered during scanning, before safety validation. +/// This is the Scanner's output type, distinct from the validated CleanupItem. +public struct ScanItem: Sendable { + public let url: URL + public let size: Int64 + public let category: CleanupCategory + public let isDirectory: Bool + public let modificationDate: Date + + public init(url: URL, size: Int64, category: CleanupCategory, isDirectory: Bool, modificationDate: Date) { + self.url = url + self.size = size + self.category = category + self.isDirectory = isDirectory + self.modificationDate = modificationDate + } + + /// Converts this raw scan item to a validated CleanupItem. + public func toCleanupItem(risk: CleanupRisk? = nil) -> CleanupItem { + CleanupItem( + url: url, + size: size, + category: category, + risk: risk, + isDirectory: isDirectory, + modificationDate: modificationDate + ) + } +} diff --git a/MacSweep/Core/Scanner/ScanProgress.swift b/MacSweep/Core/Scanner/ScanProgress.swift new file mode 100644 index 0000000..1363123 --- /dev/null +++ b/MacSweep/Core/Scanner/ScanProgress.swift @@ -0,0 +1,38 @@ +import Foundation + +/// Real-time progress reporting state for active scan operations. +public struct ScanProgress: Sendable { + public let currentCategory: CleanupCategory? + public let currentPath: String + public let scannedBytes: Int64 + public let scannedItemsCount: Int + public let isComplete: Bool + + /// Formatted scanned bytes for display. + public var formattedScannedSize: String { + ByteFormatter.format(scannedBytes) + } + + public init( + currentCategory: CleanupCategory? = nil, + currentPath: String = "", + scannedBytes: Int64 = 0, + scannedItemsCount: Int = 0, + isComplete: Bool = false + ) { + self.currentCategory = currentCategory + self.currentPath = currentPath + self.scannedBytes = scannedBytes + self.scannedItemsCount = scannedItemsCount + self.isComplete = isComplete + } + + /// Creates a completed progress state. + public static func completed(totalBytes: Int64, totalItems: Int) -> ScanProgress { + ScanProgress( + scannedBytes: totalBytes, + scannedItemsCount: totalItems, + isComplete: true + ) + } +} diff --git a/MacSweep/Core/Scanner/ScanResult.swift b/MacSweep/Core/Scanner/ScanResult.swift new file mode 100644 index 0000000..69dcd68 --- /dev/null +++ b/MacSweep/Core/Scanner/ScanResult.swift @@ -0,0 +1,44 @@ +import Foundation + +/// Aggregate container for scan results from a complete Smart Scan operation. +public struct ScanResult: Sendable { + public var items: [CleanupItem] + public let duration: TimeInterval + public let scannedCategories: [CleanupCategory] + + /// Total bytes across all discovered items. + public var totalBytes: Int64 { + items.reduce(0) { $0 + $1.size } + } + + /// Total bytes for selected items only. + public var selectedBytes: Int64 { + items.filter(\.isSelected).reduce(0) { $0 + $1.size } + } + + /// Number of selected items. + public var selectedCount: Int { + items.filter(\.isSelected).count + } + + /// Items grouped by category. + public var groupedByCategory: [CleanupCategory: [CleanupItem]] { + Dictionary(grouping: items, by: \.category) + } + + /// Formatted total size string. + public var formattedTotalSize: String { + ByteFormatter.format(totalBytes) + } + + /// Formatted selected size string. + public var formattedSelectedSize: String { + ByteFormatter.format(selectedBytes) + } + + public init(items: [CleanupItem] = [], duration: TimeInterval = 0, scannedCategories: [CleanupCategory] = []) { + self.items = items + self.duration = duration + self.scannedCategories = scannedCategories + } +} diff --git a/MacSweep/Core/Scanner/Scanners/CacheScanner.swift b/MacSweep/Core/Scanner/Scanners/CacheScanner.swift new file mode 100644 index 0000000..cd8ae1d --- /dev/null +++ b/MacSweep/Core/Scanner/Scanners/CacheScanner.swift @@ -0,0 +1,67 @@ +import Foundation +import OSLog + +/// Scans user and system cache directories for reclaimable temporary files. +public struct CacheScanner: Sendable { + private let fileManager = FileManager.default + + /// Target cache directories to scan. + private var cacheDirectories: [URL] { + let home = fileManager.homeDirectoryForCurrentUser + let library = home.appendingPathComponent("Library") + return [ + library.appendingPathComponent("Caches"), + home.appendingPathComponent(".cache") + ] + } + + /// Scans cache directories and returns discovered cleanable items. + public func scan() async -> [ScanItem] { + var items: [ScanItem] = [] + + for cacheDir in cacheDirectories { + guard fileManager.fileExists(atPath: cacheDir.path) else { continue } + Logger.scanner.debug("Scanning cache directory: \(cacheDir.path, privacy: .private)") + + do { + let contents = try fileManager.contentsOfDirectory( + at: cacheDir, + includingPropertiesForKeys: [.isDirectoryKey, .totalFileAllocatedSizeKey, .contentModificationDateKey], + options: [.skipsHiddenFiles] + ) + + for itemURL in contents { + if Task.isCancelled { return items } + + let values = try? itemURL.resourceValues(forKeys: [.isDirectoryKey]) + let isDir = values?.isDirectory ?? false + let size: Int64 + + if isDir { + size = await FileEnumerator.directorySize(itemURL) + } else { + let fileValues = try? itemURL.resourceValues(forKeys: [.totalFileAllocatedSizeKey, .fileSizeKey]) + size = Int64(fileValues?.totalFileAllocatedSize ?? fileValues?.fileSize ?? 0) + } + + guard size > 0 else { continue } + + let modDate = (try? itemURL.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast + + items.append(ScanItem( + url: itemURL, + size: size, + category: .systemCache, + isDirectory: isDir, + modificationDate: modDate + )) + } + } catch { + Logger.scanner.warning("Failed to enumerate cache directory: \(error.localizedDescription)") + } + } + + Logger.scanner.info("Cache scan complete: \(items.count) items, \(ByteFormatter.format(items.reduce(0) { $0 + $1.size }))") + return items + } +} diff --git a/MacSweep/Core/Scanner/Scanners/DockerScanner.swift b/MacSweep/Core/Scanner/Scanners/DockerScanner.swift new file mode 100644 index 0000000..cd8a5d6 --- /dev/null +++ b/MacSweep/Core/Scanner/Scanners/DockerScanner.swift @@ -0,0 +1,40 @@ +import Foundation +import OSLog + +/// Scans Docker Desktop build caches and data. +public struct DockerScanner: Sendable { + private let fileManager = FileManager.default + + private var targetPaths: [(URL, String)] { + let home = fileManager.homeDirectoryForCurrentUser + return [ + (home.appendingPathComponent("Library/Containers/com.docker.docker/Data"), "Docker Data"), + (home.appendingPathComponent(".docker"), "Docker Config") + ] + } + + public func scan() async -> [ScanItem] { + var items: [ScanItem] = [] + + for (path, label) in targetPaths { + guard fileManager.fileExists(atPath: path.path) else { continue } + Logger.scanner.debug("Scanning Docker path: \(label)") + + let size = await FileEnumerator.directorySize(path) + guard size > 0 else { continue } + + let modDate = (try? path.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast + + items.append(ScanItem( + url: path, + size: size, + category: .developerDocker, + isDirectory: true, + modificationDate: modDate + )) + } + + Logger.scanner.info("Docker scan complete: \(items.count) items") + return items + } +} diff --git a/MacSweep/Core/Scanner/Scanners/GradleScanner.swift b/MacSweep/Core/Scanner/Scanners/GradleScanner.swift new file mode 100644 index 0000000..eb7023c --- /dev/null +++ b/MacSweep/Core/Scanner/Scanners/GradleScanner.swift @@ -0,0 +1,42 @@ +import Foundation +import OSLog + +/// Scans Gradle and Android build caches for reclaimable artifacts. +public struct GradleScanner: Sendable { + private let fileManager = FileManager.default + + private var targetPaths: [(URL, String)] { + let home = fileManager.homeDirectoryForCurrentUser + return [ + (home.appendingPathComponent(".gradle/caches"), "Gradle Caches"), + (home.appendingPathComponent(".gradle/wrapper/dists"), "Gradle Wrapper Distributions"), + (home.appendingPathComponent(".android/build-cache"), "Android Build Cache"), + (home.appendingPathComponent(".android/cache"), "Android SDK Cache") + ] + } + + public func scan() async -> [ScanItem] { + var items: [ScanItem] = [] + + for (path, label) in targetPaths { + guard fileManager.fileExists(atPath: path.path) else { continue } + Logger.scanner.debug("Scanning Gradle path: \(label)") + + let size = await FileEnumerator.directorySize(path) + guard size > 0 else { continue } + + let modDate = (try? path.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast + + items.append(ScanItem( + url: path, + size: size, + category: .developerGradle, + isDirectory: true, + modificationDate: modDate + )) + } + + Logger.scanner.info("Gradle scan complete: \(items.count) items") + return items + } +} diff --git a/MacSweep/Core/Scanner/Scanners/HomebrewScanner.swift b/MacSweep/Core/Scanner/Scanners/HomebrewScanner.swift new file mode 100644 index 0000000..f6aa940 --- /dev/null +++ b/MacSweep/Core/Scanner/Scanners/HomebrewScanner.swift @@ -0,0 +1,40 @@ +import Foundation +import OSLog + +/// Scans Homebrew download and formula caches. +public struct HomebrewScanner: Sendable { + private let fileManager = FileManager.default + + private var targetPaths: [(URL, String)] { + let home = fileManager.homeDirectoryForCurrentUser + return [ + (home.appendingPathComponent("Library/Caches/Homebrew"), "Homebrew Cache"), + (home.appendingPathComponent("Library/Logs/Homebrew"), "Homebrew Logs") + ] + } + + public func scan() async -> [ScanItem] { + var items: [ScanItem] = [] + + for (path, label) in targetPaths { + guard fileManager.fileExists(atPath: path.path) else { continue } + Logger.scanner.debug("Scanning Homebrew path: \(label)") + + let size = await FileEnumerator.directorySize(path) + guard size > 0 else { continue } + + let modDate = (try? path.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast + + items.append(ScanItem( + url: path, + size: size, + category: .developerHomebrew, + isDirectory: true, + modificationDate: modDate + )) + } + + Logger.scanner.info("Homebrew scan complete: \(items.count) items") + return items + } +} diff --git a/MacSweep/Core/Scanner/Scanners/LogScanner.swift b/MacSweep/Core/Scanner/Scanners/LogScanner.swift new file mode 100644 index 0000000..6a7862a --- /dev/null +++ b/MacSweep/Core/Scanner/Scanners/LogScanner.swift @@ -0,0 +1,42 @@ +import Foundation +import OSLog + +/// Scans user log directories for reclaimable log files. +public struct LogScanner: Sendable { + private let fileManager = FileManager.default + + private var logDirectories: [URL] { + let home = fileManager.homeDirectoryForCurrentUser + return [ + home.appendingPathComponent("Library/Logs") + ] + } + + public func scan() async -> [ScanItem] { + var items: [ScanItem] = [] + + for logDir in logDirectories { + guard fileManager.fileExists(atPath: logDir.path) else { continue } + Logger.scanner.debug("Scanning log directory: \(logDir.path, privacy: .private)") + + for await metadata in FileEnumerator.enumerate(directory: logDir, includeHidden: false) { + if Task.isCancelled { return items } + guard !metadata.isDirectory && metadata.size > 0 else { continue } + + let ext = metadata.url.pathExtension.lowercased() + guard ["log", "txt", "crash", "diag", "ips", "spin"].contains(ext) else { continue } + + items.append(ScanItem( + url: metadata.url, + size: metadata.size, + category: .userLogs, + isDirectory: false, + modificationDate: metadata.modificationDate + )) + } + } + + Logger.scanner.info("Log scan complete: \(items.count) items") + return items + } +} diff --git a/MacSweep/Core/Scanner/Scanners/NodeScanner.swift b/MacSweep/Core/Scanner/Scanners/NodeScanner.swift new file mode 100644 index 0000000..d488738 --- /dev/null +++ b/MacSweep/Core/Scanner/Scanners/NodeScanner.swift @@ -0,0 +1,43 @@ +import Foundation +import OSLog + +/// Scans Node.js ecosystem caches (npm, pnpm, Yarn, Bun). +public struct NodeScanner: Sendable { + private let fileManager = FileManager.default + + private var targetPaths: [(URL, String)] { + let home = fileManager.homeDirectoryForCurrentUser + return [ + (home.appendingPathComponent(".npm/_cacache"), "npm Cache"), + (home.appendingPathComponent("Library/Caches/Yarn"), "Yarn Cache"), + (home.appendingPathComponent("Library/pnpm/store"), "pnpm Store"), + (home.appendingPathComponent(".local/share/pnpm/store"), "pnpm Store (XDG)"), + (home.appendingPathComponent(".bun/install/cache"), "Bun Cache") + ] + } + + public func scan() async -> [ScanItem] { + var items: [ScanItem] = [] + + for (path, label) in targetPaths { + guard fileManager.fileExists(atPath: path.path) else { continue } + Logger.scanner.debug("Scanning Node path: \(label)") + + let size = await FileEnumerator.directorySize(path) + guard size > 0 else { continue } + + let modDate = (try? path.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast + + items.append(ScanItem( + url: path, + size: size, + category: .developerNode, + isDirectory: true, + modificationDate: modDate + )) + } + + Logger.scanner.info("Node scan complete: \(items.count) items") + return items + } +} diff --git a/MacSweep/Core/Scanner/Scanners/TrashScanner.swift b/MacSweep/Core/Scanner/Scanners/TrashScanner.swift new file mode 100644 index 0000000..89f4bbe --- /dev/null +++ b/MacSweep/Core/Scanner/Scanners/TrashScanner.swift @@ -0,0 +1,58 @@ +import Foundation +import OSLog + +/// Scans the user's Trash directory for reclaimable items. +public struct TrashScanner: Sendable { + private let fileManager = FileManager.default + + private var trashURL: URL { + fileManager.homeDirectoryForCurrentUser.appendingPathComponent(".Trash") + } + + public func scan() async -> [ScanItem] { + var items: [ScanItem] = [] + + guard fileManager.fileExists(atPath: trashURL.path) else { return items } + Logger.scanner.debug("Scanning Trash directory") + + do { + let contents = try fileManager.contentsOfDirectory( + at: trashURL, + includingPropertiesForKeys: [.isDirectoryKey, .totalFileAllocatedSizeKey, .contentModificationDateKey], + options: [] + ) + + for itemURL in contents { + if Task.isCancelled { return items } + + let values = try? itemURL.resourceValues(forKeys: [.isDirectoryKey]) + let isDir = values?.isDirectory ?? false + let size: Int64 + + if isDir { + size = await FileEnumerator.directorySize(itemURL) + } else { + let fileValues = try? itemURL.resourceValues(forKeys: [.totalFileAllocatedSizeKey, .fileSizeKey]) + size = Int64(fileValues?.totalFileAllocatedSize ?? fileValues?.fileSize ?? 0) + } + + guard size > 0 else { continue } + + let modDate = (try? itemURL.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast + + items.append(ScanItem( + url: itemURL, + size: size, + category: .trash, + isDirectory: isDir, + modificationDate: modDate + )) + } + } catch { + Logger.scanner.warning("Failed to enumerate Trash: \(error.localizedDescription)") + } + + Logger.scanner.info("Trash scan complete: \(items.count) items") + return items + } +} diff --git a/MacSweep/Core/Scanner/Scanners/XcodeScanner.swift b/MacSweep/Core/Scanner/Scanners/XcodeScanner.swift new file mode 100644 index 0000000..c58a23b --- /dev/null +++ b/MacSweep/Core/Scanner/Scanners/XcodeScanner.swift @@ -0,0 +1,44 @@ +import Foundation +import OSLog + +/// Scans Xcode-related developer caches including DerivedData, Archives, and SPM caches. +public struct XcodeScanner: Sendable { + private let fileManager = FileManager.default + + private var targetPaths: [(URL, String)] { + let home = fileManager.homeDirectoryForCurrentUser + let dev = home.appendingPathComponent("Library/Developer/Xcode") + return [ + (dev.appendingPathComponent("DerivedData"), "DerivedData"), + (dev.appendingPathComponent("Archives"), "Archives"), + (home.appendingPathComponent("Library/Caches/org.swift.swiftpm"), "Swift Package Manager Cache"), + (home.appendingPathComponent("Library/Caches/CocoaPods"), "CocoaPods Cache"), + (home.appendingPathComponent("Library/Developer/CoreSimulator/Caches"), "Simulator Caches") + ] + } + + public func scan() async -> [ScanItem] { + var items: [ScanItem] = [] + + for (path, label) in targetPaths { + guard fileManager.fileExists(atPath: path.path) else { continue } + Logger.scanner.debug("Scanning Xcode path: \(label)") + + let size = await FileEnumerator.directorySize(path) + guard size > 0 else { continue } + + let modDate = (try? path.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast + + items.append(ScanItem( + url: path, + size: size, + category: .developerXcode, + isDirectory: true, + modificationDate: modDate + )) + } + + Logger.scanner.info("Xcode scan complete: \(items.count) items") + return items + } +} From 92b27db70719a0109be3fa56f261c48bbe13fe75 Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Mon, 24 Aug 2026 08:59:59 +0530 Subject: [PATCH 08/55] feat(core): implement cleanup engine, deletion service, and Trash relocation service --- MacSweep/Core/Cleaner/CleanCoordinator.swift | 36 +++++++++++++ MacSweep/Core/Cleaner/CleanEngine.swift | 54 +++++++++++++++++++ MacSweep/Core/Cleaner/CleanOperation.swift | 20 +++++++ MacSweep/Core/Cleaner/CleanResult.swift | 37 +++++++++++++ MacSweep/Core/Cleaner/DeletionService.swift | 56 ++++++++++++++++++++ MacSweep/Core/Cleaner/TrashService.swift | 22 ++++++++ 6 files changed, 225 insertions(+) create mode 100644 MacSweep/Core/Cleaner/CleanCoordinator.swift create mode 100644 MacSweep/Core/Cleaner/CleanEngine.swift create mode 100644 MacSweep/Core/Cleaner/CleanOperation.swift create mode 100644 MacSweep/Core/Cleaner/CleanResult.swift create mode 100644 MacSweep/Core/Cleaner/DeletionService.swift create mode 100644 MacSweep/Core/Cleaner/TrashService.swift diff --git a/MacSweep/Core/Cleaner/CleanCoordinator.swift b/MacSweep/Core/Cleaner/CleanCoordinator.swift new file mode 100644 index 0000000..6157050 --- /dev/null +++ b/MacSweep/Core/Cleaner/CleanCoordinator.swift @@ -0,0 +1,36 @@ +import Foundation +import OSLog + +/// Coordinates cleanup lifecycle, managing active operations and cancellation. +public actor CleanCoordinator { + private let cleanEngine: CleanEngine + private var currentCleanTask: Task? + + public var isCleaning: Bool { + currentCleanTask != nil && currentCleanTask?.isCancelled == false + } + + public init(cleanEngine: CleanEngine) { + self.cleanEngine = cleanEngine + } + + /// Starts a cleanup operation for the given items. + public func startCleanup(items: [CleanupItem]) async -> CleanResult { + cancelCurrentCleanup() + + let task = Task { + await cleanEngine.clean(items: items) + } + currentCleanTask = task + let result = await task.value + currentCleanTask = nil + return result + } + + /// Cancels any currently running cleanup operation. + public func cancelCurrentCleanup() { + currentCleanTask?.cancel() + currentCleanTask = nil + Logger.cleaner.info("Cleanup cancelled") + } +} diff --git a/MacSweep/Core/Cleaner/CleanEngine.swift b/MacSweep/Core/Cleaner/CleanEngine.swift new file mode 100644 index 0000000..20d8af0 --- /dev/null +++ b/MacSweep/Core/Cleaner/CleanEngine.swift @@ -0,0 +1,54 @@ +import Foundation +import OSLog + +/// Central cleanup execution engine that processes user-confirmed cleanup operations. +/// Only operates on items that have passed SafetyValidator and received user confirmation. +public final class CleanEngine: Sendable { + private let deletionService: DeletionService + + public init(safetyValidator: SafetyValidator) { + self.deletionService = DeletionService(safetyValidator: safetyValidator) + } + + /// Executes cleanup operations for all selected items. + /// - Parameter items: The user-confirmed cleanup items (only selected items are processed). + /// - Returns: An aggregate `CleanResult`. + public func clean(items: [CleanupItem]) async -> CleanResult { + let startTime = Date() + let selectedItems = items.filter(\.isSelected) + + Logger.cleaner.info("Starting cleanup: \(selectedItems.count) items") + + var successCount = 0 + var failureCount = 0 + var totalReclaimed: Int64 = 0 + var failures: [CleanOperationResult] = [] + + for item in selectedItems { + if Task.isCancelled { break } + + let result = deletionService.safeDelete(item.url) + + if result.success { + successCount += 1 + totalReclaimed += item.size + } else { + failureCount += 1 + failures.append(result) + } + } + + let duration = Date().timeIntervalSince(startTime) + + Logger.cleaner.info("Cleanup complete: \(successCount) succeeded, \(failureCount) failed, \(ByteFormatter.format(totalReclaimed)) reclaimed") + + return CleanResult( + totalItemsProcessed: selectedItems.count, + successCount: successCount, + failureCount: failureCount, + totalBytesReclaimed: totalReclaimed, + failures: failures, + duration: duration + ) + } +} diff --git a/MacSweep/Core/Cleaner/CleanOperation.swift b/MacSweep/Core/Cleaner/CleanOperation.swift new file mode 100644 index 0000000..e6a844b --- /dev/null +++ b/MacSweep/Core/Cleaner/CleanOperation.swift @@ -0,0 +1,20 @@ +import Foundation + +/// Represents a queued cleanup operation for a single item. +public struct CleanOperation: Identifiable, Sendable { + public let id: UUID + public let item: CleanupItem + public let method: CleanMethod + + public init(id: UUID = UUID(), item: CleanupItem, method: CleanMethod = .moveToTrash) { + self.id = id + self.item = item + self.method = method + } +} + +/// Method of file removal. +public enum CleanMethod: String, Sendable { + case moveToTrash + case permanentDelete +} diff --git a/MacSweep/Core/Cleaner/CleanResult.swift b/MacSweep/Core/Cleaner/CleanResult.swift new file mode 100644 index 0000000..8046541 --- /dev/null +++ b/MacSweep/Core/Cleaner/CleanResult.swift @@ -0,0 +1,37 @@ +import Foundation + +/// Aggregate result of a cleanup operation across multiple items. +public struct CleanResult: Sendable { + public let totalItemsProcessed: Int + public let successCount: Int + public let failureCount: Int + public let totalBytesReclaimed: Int64 + public let failures: [CleanOperationResult] + public let duration: TimeInterval + + /// Formatted reclaimed space string. + public var formattedReclaimed: String { + ByteFormatter.format(totalBytesReclaimed) + } + + /// Whether all operations succeeded. + public var isFullSuccess: Bool { + failureCount == 0 + } + + public init( + totalItemsProcessed: Int, + successCount: Int, + failureCount: Int, + totalBytesReclaimed: Int64, + failures: [CleanOperationResult] = [], + duration: TimeInterval = 0 + ) { + self.totalItemsProcessed = totalItemsProcessed + self.successCount = successCount + self.failureCount = failureCount + self.totalBytesReclaimed = totalBytesReclaimed + self.failures = failures + self.duration = duration + } +} diff --git a/MacSweep/Core/Cleaner/DeletionService.swift b/MacSweep/Core/Cleaner/DeletionService.swift new file mode 100644 index 0000000..ea41da9 --- /dev/null +++ b/MacSweep/Core/Cleaner/DeletionService.swift @@ -0,0 +1,56 @@ +import Foundation +import OSLog + +/// Provides safe, validated file deletion operations. +/// All deletions pass through SafetyValidator before execution. +public struct DeletionService: Sendable { + private let safetyValidator: SafetyValidator + + public init(safetyValidator: SafetyValidator) { + self.safetyValidator = safetyValidator + } + + /// Safely deletes a file or directory by moving it to Trash. + /// - Parameter url: The file URL to remove. + /// - Returns: A `CleanOperationResult` indicating success or failure. + public func safeDelete(_ url: URL) -> CleanOperationResult { + // Final safety check before deletion + let validation = safetyValidator.validate(url) + guard validation == .approved else { + return CleanOperationResult( + url: url, + success: false, + error: "Safety validation failed" + ) + } + + // Verify file still exists (may have been removed during review) + guard FileManager.default.fileExists(atPath: url.path) else { + return CleanOperationResult( + url: url, + success: false, + error: "File no longer exists" + ) + } + + do { + _ = try TrashService.moveToTrash(url) + return CleanOperationResult(url: url, success: true) + } catch { + return CleanOperationResult(url: url, success: false, error: error.localizedDescription) + } + } +} + +/// Result of a single cleanup operation. +public struct CleanOperationResult: Sendable { + public let url: URL + public let success: Bool + public let error: String? + + public init(url: URL, success: Bool, error: String? = nil) { + self.url = url + self.success = success + self.error = error + } +} diff --git a/MacSweep/Core/Cleaner/TrashService.swift b/MacSweep/Core/Cleaner/TrashService.swift new file mode 100644 index 0000000..c29831c --- /dev/null +++ b/MacSweep/Core/Cleaner/TrashService.swift @@ -0,0 +1,22 @@ +import Foundation +import OSLog + +/// Handles safe file relocation to the macOS Trash using native FileManager APIs. +/// This is the primary deletion mechanism — never uses shell commands. +public struct TrashService: Sendable { + /// Moves a file or directory to the macOS Trash. + /// - Parameter url: The file or directory URL to trash. + /// - Returns: The resulting URL in the Trash directory. + /// - Throws: `MacSweepError.trashFailed` if the operation fails. + public static func moveToTrash(_ url: URL) throws -> URL { + var resultURL: NSURL? + do { + try FileManager.default.trashItem(at: url, resultingItemURL: &resultURL) + Logger.cleaner.info("Moved to Trash: \(url.lastPathComponent, privacy: .public)") + return (resultURL as URL?) ?? url + } catch { + Logger.cleaner.error("Trash failed for \(url.path, privacy: .private): \(error.localizedDescription)") + throw MacSweepError.trashFailed(path: url.path, reason: error.localizedDescription) + } + } +} From 5da011130c3cfeb9304505301c9e2674f4315f7c Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Tue, 25 Aug 2026 23:58:24 +0530 Subject: [PATCH 09/55] feat(config): add App Info.plist, Localizable resources, and privacy manifest --- .gitignore | 27 ++ MacSweep/Info.plist | 34 +++ MacSweep/Resources/Localizable.xcstrings | 333 +++++++++++++++++++++++ MacSweep/Resources/PrivacyInfo.xcprivacy | 12 + 4 files changed, 406 insertions(+) create mode 100644 MacSweep/Info.plist create mode 100644 MacSweep/Resources/Localizable.xcstrings create mode 100644 MacSweep/Resources/PrivacyInfo.xcprivacy diff --git a/.gitignore b/.gitignore index e69de29..2124f56 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,27 @@ +# macOS +.DS_Store +.AppleDouble +.LSOverride + +# Xcode +build/ +DerivedData/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.hmap +*.ipa +*.dSYM.zip +*.dSYM +xcuserdata/ +*.xcuserdata + +# Swift Package Manager +.build/ +.swiftpm/ + +# User-specific +*.xcuserstate diff --git a/MacSweep/Info.plist b/MacSweep/Info.plist new file mode 100644 index 0000000..0881fab --- /dev/null +++ b/MacSweep/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2026 MacSweep. All rights reserved. + NSAppleEventsUsageDescription + MacSweep needs access to perform cleanup and system maintenance tasks. + NSSystemAdministrationUsageDescription + MacSweep requires administrative privileges to clean system cache files and manage startup items. + NSMainStoryboardFile + + NSPrincipalClass + NSApplication + + diff --git a/MacSweep/Resources/Localizable.xcstrings b/MacSweep/Resources/Localizable.xcstrings new file mode 100644 index 0000000..879fe31 --- /dev/null +++ b/MacSweep/Resources/Localizable.xcstrings @@ -0,0 +1,333 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "" : { + + }, + "%@ — %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$@ — %2$@" + } + } + } + }, + "%@ free" : { + + }, + "%@ Free" : { + + }, + "%@ used" : { + + }, + "%@ Used" : { + + }, + "%@: %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$@: %2$@" + } + } + } + }, + "%lld" : { + + }, + "%lld items" : { + + }, + "%lld items • %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$lld items • %2$@" + } + } + } + }, + "%lld items ready for cleanup" : { + + }, + "%lld items scanned (%@)" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$lld items scanned (%2$@)" + } + } + } + }, + "%lld items selected (%@)" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$lld items selected (%2$@)" + } + } + } + }, + "%lld/%lld items" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$lld/%2$lld items" + } + } + } + }, + "%lld%%" : { + + }, + "> 1 GB" : { + + }, + "> 50 MB" : { + + }, + "> 100 MB" : { + + }, + "> 500 MB" : { + + }, + "About" : { + + }, + "Alerts & System Banners" : { + + }, + "App Uninstaller" : { + + }, + "Application Bundle" : { + + }, + "Auto Select" : { + + }, + "Automatically check for updates" : { + + }, + "Capacity" : { + + }, + "Clean Xcode DerivedData, Node caches, Gradle builds, Homebrew & Docker data" : { + + }, + "Cleanable Junk" : { + + }, + "Cleaned" : { + + }, + "Cleanup Complete!" : { + + }, + "Clear web browser caches, cookies, and tracking artifacts" : { + + }, + "Completely remove applications along with their hidden leftovers & caches" : { + + }, + "Completely Uninstall" : { + + }, + "Dashboard" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dashboard" + } + } + } + }, + "Developer Cleaner" : { + + }, + "Distribution" : { + + }, + "Duplicates Finder" : { + + }, + "Enable cleanup completion notifications" : { + + }, + "Failed" : { + + }, + "Find and remove identical file copies across folders" : { + + }, + "Find files consuming substantial disk space" : { + + }, + "Full Disk Access is required for MacSweep to scan and clean system-wide caches and logs." : { + + }, + "Full Disk Access Required" : { + + }, + "General" : { + + }, + "High-performance macOS system cleaner & optimization suite." : { + + }, + "Installed Files & Leftovers" : { + + }, + "Keep Newest (Select Older)" : { + + }, + "Keep Oldest (Select Newer)" : { + + }, + "Large File Minimum Size: %lld MB" : { + + }, + "Large Files Finder" : { + + }, + "Launch MacSweep at login" : { + + }, + "MacSweep" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "MacSweep" + } + } + } + }, + "MacSweep Overview" : { + + }, + "Manage background LaunchAgents that run when logging into macOS" : { + + }, + "Notifications" : { + + }, + "Open Privacy & Security Settings" : { + + }, + "Optimize system performance, clean junk files, analyze disk usage, and manage developer caches." : { + + }, + "Original" : { + + }, + "Permissions & Disk Access" : { + + }, + "Privacy" : { + + }, + "Privacy Cleaner" : { + + }, + "Quick Tools" : { + + }, + "Real-time system health, storage allocation, and maintenance tools" : { + + }, + "Scan Results" : { + + }, + "Scanning" : { + + }, + "Scanning Mac..." : { + + }, + "Search apps..." : { + + }, + "Setup Complete!" : { + + }, + "Smart Scan" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Smart Scan" + } + } + } + }, + "Startup & Updates" : { + + }, + "Startup Items" : { + + }, + "Storage Analyzer" : { + + }, + "Successfully reclaimed %@" : { + + }, + "System caches & temporary items" : { + + }, + "System is optimized" : { + + }, + "System Optimal" : { + + }, + "System Storage" : { + + }, + "Threshold" : { + + }, + "Thresholds" : { + + }, + "To clean system caches, user logs, and developer artifacts, MacSweep needs Full Disk Access permission." : { + + }, + "Total %@" : { + + }, + "Total Size: %@" : { + + }, + "Version 1.0.0 (Build 1)" : { + + }, + "Visual disk space breakdown" : { + + }, + "Wasted %@" : { + + }, + "Welcome to MacSweep" : { + + }, + "You are ready to optimize and clean your Mac." : { + + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/MacSweep/Resources/PrivacyInfo.xcprivacy b/MacSweep/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..d2d54f1 --- /dev/null +++ b/MacSweep/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,12 @@ + + + + + NSPrivacyTracking + + NSPrivacyCollectedDataTypes + + NSPrivacyAccessedAPITypes + + + From df4348b93e4763914913452548ea94c50093e566 Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Tue, 25 Aug 2026 23:58:39 +0530 Subject: [PATCH 10/55] feat(ui): add navigation coordinator, theme modifiers, and core UI components --- .../UI/Components/ConfirmationDialog.swift | 72 +++++++++++++++++++ MacSweep/UI/Components/EmptyStateView.swift | 50 +++++++++++++ MacSweep/UI/Components/ErrorView.swift | 43 +++++++++++ MacSweep/UI/Components/IconButton.swift | 31 ++++++++ MacSweep/UI/Components/LoadingView.swift | 33 +++++++++ MacSweep/UI/Components/PrimaryButton.swift | 45 ++++++++++++ MacSweep/UI/Components/SecondaryButton.swift | 42 +++++++++++ MacSweep/UI/Modifiers/CardModifier.swift | 33 +++++++++ MacSweep/UI/Modifiers/HoverModifier.swift | 23 ++++++ .../UI/Navigation/NavigationCoordinator.swift | 16 +++++ MacSweep/UI/Navigation/NavigationItem.swift | 64 +++++++++++++++++ MacSweep/UI/Navigation/SidebarView.swift | 71 ++++++++++++++++++ MacSweep/UI/Theme/AppTheme.swift | 15 ++++ MacSweep/UI/Theme/Colors.swift | 13 ++++ MacSweep/UI/Theme/Spacing.swift | 10 +++ MacSweep/UI/Theme/Typography.swift | 10 +++ 16 files changed, 571 insertions(+) create mode 100644 MacSweep/UI/Components/ConfirmationDialog.swift create mode 100644 MacSweep/UI/Components/EmptyStateView.swift create mode 100644 MacSweep/UI/Components/ErrorView.swift create mode 100644 MacSweep/UI/Components/IconButton.swift create mode 100644 MacSweep/UI/Components/LoadingView.swift create mode 100644 MacSweep/UI/Components/PrimaryButton.swift create mode 100644 MacSweep/UI/Components/SecondaryButton.swift create mode 100644 MacSweep/UI/Modifiers/CardModifier.swift create mode 100644 MacSweep/UI/Modifiers/HoverModifier.swift create mode 100644 MacSweep/UI/Navigation/NavigationCoordinator.swift create mode 100644 MacSweep/UI/Navigation/NavigationItem.swift create mode 100644 MacSweep/UI/Navigation/SidebarView.swift create mode 100644 MacSweep/UI/Theme/AppTheme.swift create mode 100644 MacSweep/UI/Theme/Colors.swift create mode 100644 MacSweep/UI/Theme/Spacing.swift create mode 100644 MacSweep/UI/Theme/Typography.swift diff --git a/MacSweep/UI/Components/ConfirmationDialog.swift b/MacSweep/UI/Components/ConfirmationDialog.swift new file mode 100644 index 0000000..27e4c79 --- /dev/null +++ b/MacSweep/UI/Components/ConfirmationDialog.swift @@ -0,0 +1,72 @@ +import SwiftUI + +public struct ConfirmationDialog: View { + public let title: String + public let message: String + public let confirmTitle: String + public let cancelTitle: String + public let isDestructive: Bool + public let onConfirm: () -> Void + public let onCancel: () -> Void + + public init( + title: String, + message: String, + confirmTitle: String = "Clean", + cancelTitle: String = "Cancel", + isDestructive: Bool = true, + onConfirm: @escaping () -> Void, + onCancel: @escaping () -> Void + ) { + self.title = title + self.message = message + self.confirmTitle = confirmTitle + self.cancelTitle = cancelTitle + self.isDestructive = isDestructive + self.onConfirm = onConfirm + self.onCancel = onCancel + } + + public var body: some View { + VStack(spacing: 20) { + HStack(alignment: .top, spacing: 14) { + Image(systemName: isDestructive ? "trash.fill" : "info.circle.fill") + .font(.system(size: 28)) + .foregroundColor(isDestructive ? .msHighRisk : .msAccent) + + VStack(alignment: .leading, spacing: 6) { + Text(title) + .font(.system(size: 15, weight: .bold)) + .foregroundColor(.msLabel) + + Text(message) + .font(.system(size: 13)) + .foregroundColor(.msSecondaryLabel) + .fixedSize(horizontal: false, vertical: true) + } + } + + HStack(spacing: 12) { + Spacer() + + SecondaryButton(title: cancelTitle, action: onCancel) + + Button(action: onConfirm) { + Text(confirmTitle) + .font(.system(size: 13, weight: .semibold)) + .padding(.horizontal, 16) + .padding(.vertical, 7) + .background(isDestructive ? Color.msHighRisk : Color.msAccent) + .foregroundColor(.white) + .cornerRadius(8) + } + .buttonStyle(.plain) + } + } + .padding(20) + .frame(width: 420) + .background(Color.msBackground) + .cornerRadius(12) + .shadow(radius: 10) + } +} diff --git a/MacSweep/UI/Components/EmptyStateView.swift b/MacSweep/UI/Components/EmptyStateView.swift new file mode 100644 index 0000000..b0091c5 --- /dev/null +++ b/MacSweep/UI/Components/EmptyStateView.swift @@ -0,0 +1,50 @@ +import SwiftUI + +public struct EmptyStateView: View { + public let title: String + public let subtitle: String + public let iconName: String + public let buttonTitle: String? + public let buttonAction: (() -> Void)? + + public init( + title: String, + subtitle: String, + iconName: String = "tray", + buttonTitle: String? = nil, + buttonAction: (() -> Void)? = nil + ) { + self.title = title + self.subtitle = subtitle + self.iconName = iconName + self.buttonTitle = buttonTitle + self.buttonAction = buttonAction + } + + public var body: some View { + VStack(spacing: 16) { + Image(systemName: iconName) + .font(.system(size: 48, weight: .light)) + .foregroundColor(.msSecondaryLabel) + + VStack(spacing: 6) { + Text(title) + .font(.system(size: 16, weight: .semibold)) + .foregroundColor(.msLabel) + + Text(subtitle) + .font(.system(size: 13)) + .foregroundColor(.msSecondaryLabel) + .multilineTextAlignment(.center) + .frame(maxWidth: 320) + } + + if let buttonTitle = buttonTitle, let buttonAction = buttonAction { + PrimaryButton(title: buttonTitle, action: buttonAction) + .padding(.top, 8) + } + } + .padding(32) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } +} diff --git a/MacSweep/UI/Components/ErrorView.swift b/MacSweep/UI/Components/ErrorView.swift new file mode 100644 index 0000000..3012d5b --- /dev/null +++ b/MacSweep/UI/Components/ErrorView.swift @@ -0,0 +1,43 @@ +import SwiftUI + +public struct ErrorView: View { + public let title: String + public let message: String + public let retryAction: (() -> Void)? + + public init( + title: String = "An Error Occurred", + message: String, + retryAction: (() -> Void)? = nil + ) { + self.title = title + self.message = message + self.retryAction = retryAction + } + + public var body: some View { + VStack(spacing: 16) { + Image(systemName: "exclamationmark.triangle.fill") + .font(.system(size: 40)) + .foregroundColor(.msHighRisk) + + VStack(spacing: 6) { + Text(title) + .font(.system(size: 16, weight: .semibold)) + .foregroundColor(.msLabel) + + Text(message) + .font(.system(size: 13)) + .foregroundColor(.msSecondaryLabel) + .multilineTextAlignment(.center) + .frame(maxWidth: 360) + } + + if let retryAction = retryAction { + SecondaryButton(title: "Try Again", iconName: "arrow.clockwise", action: retryAction) + } + } + .padding(32) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } +} diff --git a/MacSweep/UI/Components/IconButton.swift b/MacSweep/UI/Components/IconButton.swift new file mode 100644 index 0000000..5734bae --- /dev/null +++ b/MacSweep/UI/Components/IconButton.swift @@ -0,0 +1,31 @@ +import SwiftUI + +public struct IconButton: View { + public let iconName: String + public let tooltip: String? + public let action: () -> Void + + public init( + iconName: String, + tooltip: String? = nil, + action: @escaping () -> Void + ) { + self.iconName = iconName + self.tooltip = tooltip + self.action = action + } + + public var body: some View { + Button(action: action) { + Image(systemName: iconName) + .font(.system(size: 13, weight: .regular)) + .foregroundColor(.msSecondaryLabel) + .frame(width: 28, height: 28) + .background(Color.primary.opacity(0.05)) + .clipShape(Circle()) + } + .buttonStyle(.plain) + .help(tooltip ?? "") + .hoverEffect(scale: 1.1) + } +} diff --git a/MacSweep/UI/Components/LoadingView.swift b/MacSweep/UI/Components/LoadingView.swift new file mode 100644 index 0000000..8862eaa --- /dev/null +++ b/MacSweep/UI/Components/LoadingView.swift @@ -0,0 +1,33 @@ +import SwiftUI + +public struct LoadingView: View { + public let title: String + public let subtitle: String? + + public init(title: String = "Scanning...", subtitle: String? = nil) { + self.title = title + self.subtitle = subtitle + } + + public var body: some View { + VStack(spacing: 16) { + ProgressView() + .scaleEffect(1.2) + .controlSize(.large) + + VStack(spacing: 4) { + Text(title) + .font(.system(size: 15, weight: .medium)) + .foregroundColor(.msLabel) + + if let subtitle = subtitle { + Text(subtitle) + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + } + } + .padding(24) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } +} diff --git a/MacSweep/UI/Components/PrimaryButton.swift b/MacSweep/UI/Components/PrimaryButton.swift new file mode 100644 index 0000000..6b6d7bf --- /dev/null +++ b/MacSweep/UI/Components/PrimaryButton.swift @@ -0,0 +1,45 @@ +import SwiftUI + +public struct PrimaryButton: View { + public let title: String + public let iconName: String? + public let isLoading: Bool + public let action: () -> Void + + public init( + title: String, + iconName: String? = nil, + isLoading: Bool = false, + action: @escaping () -> Void + ) { + self.title = title + self.iconName = iconName + self.isLoading = isLoading + self.action = action + } + + public var body: some View { + Button(action: action) { + HStack(spacing: 8) { + if isLoading { + ProgressView() + .controlSize(.small) + } else if let iconName = iconName { + Image(systemName: iconName) + .font(.system(size: 14, weight: .semibold)) + } + Text(title) + .font(.system(size: 13, weight: .semibold)) + } + .padding(.horizontal, 16) + .padding(.vertical, 8) + .frame(minHeight: 32) + .background(Color.msAccent) + .foregroundColor(.white) + .cornerRadius(8) + } + .buttonStyle(.plain) + .disabled(isLoading) + .hoverEffect(scale: 1.02) + } +} diff --git a/MacSweep/UI/Components/SecondaryButton.swift b/MacSweep/UI/Components/SecondaryButton.swift new file mode 100644 index 0000000..029cf1e --- /dev/null +++ b/MacSweep/UI/Components/SecondaryButton.swift @@ -0,0 +1,42 @@ +import SwiftUI + +public struct SecondaryButton: View { + public let title: String + public let iconName: String? + public let action: () -> Void + + public init( + title: String, + iconName: String? = nil, + action: @escaping () -> Void + ) { + self.title = title + self.iconName = iconName + self.action = action + } + + public var body: some View { + Button(action: action) { + HStack(spacing: 6) { + if let iconName = iconName { + Image(systemName: iconName) + .font(.system(size: 13, weight: .medium)) + } + Text(title) + .font(.system(size: 13, weight: .medium)) + } + .padding(.horizontal, 14) + .padding(.vertical, 7) + .frame(minHeight: 30) + .background(Color.primary.opacity(0.08)) + .foregroundColor(.msLabel) + .cornerRadius(8) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.primary.opacity(0.12), lineWidth: 0.5) + ) + } + .buttonStyle(.plain) + .hoverEffect(scale: 1.02) + } +} diff --git a/MacSweep/UI/Modifiers/CardModifier.swift b/MacSweep/UI/Modifiers/CardModifier.swift new file mode 100644 index 0000000..9614a7b --- /dev/null +++ b/MacSweep/UI/Modifiers/CardModifier.swift @@ -0,0 +1,33 @@ +import SwiftUI + +public struct CardModifier: ViewModifier { + var backgroundColor: Color = .msSecondaryBackground + var cornerRadius: CGFloat = AppTheme.cornerRadius + var padding: CGFloat = AppTheme.contentPadding + var borderWidth: CGFloat = 0.5 + var borderColor: Color = Color.primary.opacity(0.1) + + public func body(content: Content) -> some View { + content + .padding(padding) + .background( + RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) + .fill(backgroundColor) + ) + .overlay( + RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) + .stroke(borderColor, lineWidth: borderWidth) + ) + .shadow(color: Color.black.opacity(0.04), radius: 6, x: 0, y: 2) + } +} + +public extension View { + func cardStyle( + backgroundColor: Color = .msSecondaryBackground, + cornerRadius: CGFloat = AppTheme.cornerRadius, + padding: CGFloat = AppTheme.contentPadding + ) -> some View { + modifier(CardModifier(backgroundColor: backgroundColor, cornerRadius: cornerRadius, padding: padding)) + } +} diff --git a/MacSweep/UI/Modifiers/HoverModifier.swift b/MacSweep/UI/Modifiers/HoverModifier.swift new file mode 100644 index 0000000..1885b3c --- /dev/null +++ b/MacSweep/UI/Modifiers/HoverModifier.swift @@ -0,0 +1,23 @@ +import SwiftUI + +public struct HoverModifier: ViewModifier { + @State private var isHovered = false + var scale: CGFloat = 1.02 + var hoverColor: Color? = nil + + public func body(content: Content) -> some View { + content + .scaleEffect(isHovered ? scale : 1.0) + .background(hoverColor != nil && isHovered ? hoverColor : Color.clear) + .animation(.easeInOut(duration: 0.15), value: isHovered) + .onHover { hovering in + isHovered = hovering + } + } +} + +public extension View { + func hoverEffect(scale: CGFloat = 1.02, hoverColor: Color? = nil) -> some View { + modifier(HoverModifier(scale: scale, hoverColor: hoverColor)) + } +} diff --git a/MacSweep/UI/Navigation/NavigationCoordinator.swift b/MacSweep/UI/Navigation/NavigationCoordinator.swift new file mode 100644 index 0000000..b1d9266 --- /dev/null +++ b/MacSweep/UI/Navigation/NavigationCoordinator.swift @@ -0,0 +1,16 @@ +import SwiftUI +import Combine + +@MainActor +public final class NavigationCoordinator: ObservableObject { + @Published public var selectedItem: NavigationItem? = .dashboard + @Published public var showOnboarding: Bool = false + + public init(selectedItem: NavigationItem? = .dashboard) { + self.selectedItem = selectedItem + } + + public func navigate(to item: NavigationItem) { + self.selectedItem = item + } +} diff --git a/MacSweep/UI/Navigation/NavigationItem.swift b/MacSweep/UI/Navigation/NavigationItem.swift new file mode 100644 index 0000000..7689c14 --- /dev/null +++ b/MacSweep/UI/Navigation/NavigationItem.swift @@ -0,0 +1,64 @@ +import SwiftUI + +public enum NavigationGroup: String, CaseIterable, Identifiable { + case overview = "Overview" + case cleanup = "Cleanup" + case tools = "Tools" + case preferences = "Preferences" + + public var id: String { rawValue } +} + +public enum NavigationItem: String, CaseIterable, Identifiable, Hashable, Sendable { + case dashboard + case smartScan + case storageAnalyzer + case duplicates + case largeFiles + case developerCleaner + case startupItems + case uninstaller + case privacy + case settings + + public var id: String { rawValue } + + public var displayName: String { + switch self { + case .dashboard: return "Dashboard" + case .smartScan: return "Smart Scan" + case .storageAnalyzer: return "Storage Analyzer" + case .duplicates: return "Duplicates Finder" + case .largeFiles: return "Large Files" + case .developerCleaner: return "Developer Cleaner" + case .startupItems: return "Startup Items" + case .uninstaller: return "App Uninstaller" + case .privacy: return "Privacy Cleaner" + case .settings: return "Settings" + } + } + + public var iconName: String { + switch self { + case .dashboard: return "gauge.medium" + case .smartScan: return "sparkles" + case .storageAnalyzer: return "chart.pie.fill" + case .duplicates: return "doc.on.doc.fill" + case .largeFiles: return "doc.badge.arrow.up.fill" + case .developerCleaner: return "hammer.fill" + case .startupItems: return "bolt.fill" + case .uninstaller: return "trash.square.fill" + case .privacy: return "hand.raised.fill" + case .settings: return "gearshape.fill" + } + } + + public var group: NavigationGroup { + switch self { + case .dashboard: return .overview + case .smartScan, .developerCleaner, .privacy: return .cleanup + case .storageAnalyzer, .duplicates, .largeFiles, .startupItems, .uninstaller: return .tools + case .settings: return .preferences + } + } +} diff --git a/MacSweep/UI/Navigation/SidebarView.swift b/MacSweep/UI/Navigation/SidebarView.swift new file mode 100644 index 0000000..5f8e547 --- /dev/null +++ b/MacSweep/UI/Navigation/SidebarView.swift @@ -0,0 +1,71 @@ +import SwiftUI + +public struct SidebarView: View { + @Binding public var selection: NavigationItem? + @EnvironmentObject private var environment: AppEnvironment + + @State private var diskSpace: DiskSpace? + + public init(selection: Binding) { + self._selection = selection + } + + public var body: some View { + VStack(spacing: 0) { + List(selection: $selection) { + ForEach(NavigationGroup.allCases) { group in + Section(header: Text(group.rawValue).font(.system(size: 11, weight: .bold)).foregroundColor(.msSecondaryLabel)) { + ForEach(NavigationItem.allCases.filter { $0.group == group }) { item in + NavigationLink(value: item) { + Label { + Text(item.displayName) + .font(.system(size: 13, weight: .medium)) + } icon: { + Image(systemName: item.iconName) + .foregroundColor(selection == item ? .msAccent : .msSecondaryLabel) + } + } + .tag(item) + } + } + } + } + .listStyle(.sidebar) + + Divider() + + // Disk Usage Footer + if let diskSpace = diskSpace { + VStack(alignment: .leading, spacing: 8) { + HStack { + Image(systemName: "internaldrive.fill") + .foregroundColor(.msAccent) + Text(diskSpace.volumeName) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.msLabel) + Spacer() + Text("\(diskSpace.formattedAvailable) free") + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + } + + ProgressView(value: diskSpace.usedPercentage, total: 1.0) + .tint(diskSpace.usedPercentage > 0.9 ? .msHighRisk : (diskSpace.usedPercentage > 0.75 ? .msCaution : .msAccent)) + + HStack { + Text("\(diskSpace.formattedUsed) used") + Spacer() + Text("Total \(diskSpace.formattedTotal)") + } + .font(.system(size: 10)) + .foregroundColor(.msSecondaryLabel) + } + .padding(12) + .background(Color.msSecondaryBackground.opacity(0.6)) + } + } + .task { + diskSpace = try? environment.diskSpaceService.getDiskSpace() + } + } +} diff --git a/MacSweep/UI/Theme/AppTheme.swift b/MacSweep/UI/Theme/AppTheme.swift new file mode 100644 index 0000000..0de00f3 --- /dev/null +++ b/MacSweep/UI/Theme/AppTheme.swift @@ -0,0 +1,15 @@ +import SwiftUI + +/// Centralized theme providing consistent styling tokens across MacSweep. +public enum AppTheme { + /// Standard corner radius for card components. + public static let cornerRadius: CGFloat = 12 + /// Standard padding for content areas. + public static let contentPadding: CGFloat = 16 + /// Standard spacing between list items. + public static let itemSpacing: CGFloat = 8 + /// Standard section spacing. + public static let sectionSpacing: CGFloat = 20 + /// Animation duration for micro-interactions. + public static let animationDuration: Double = 0.25 +} diff --git a/MacSweep/UI/Theme/Colors.swift b/MacSweep/UI/Theme/Colors.swift new file mode 100644 index 0000000..05cc3b8 --- /dev/null +++ b/MacSweep/UI/Theme/Colors.swift @@ -0,0 +1,13 @@ +import SwiftUI + +public extension Color { + static let msAccent = Color.accentColor + static let msSafe = Color.green + static let msCaution = Color.orange + static let msHighRisk = Color.red + static let msBackground = Color(nsColor: .windowBackgroundColor) + static let msSecondaryBackground = Color(nsColor: .controlBackgroundColor) + static let msLabel = Color(nsColor: .labelColor) + static let msSecondaryLabel = Color(nsColor: .secondaryLabelColor) + static let msSeparator = Color(nsColor: .separatorColor) +} diff --git a/MacSweep/UI/Theme/Spacing.swift b/MacSweep/UI/Theme/Spacing.swift new file mode 100644 index 0000000..80bdd34 --- /dev/null +++ b/MacSweep/UI/Theme/Spacing.swift @@ -0,0 +1,10 @@ +import SwiftUI + +public enum Spacing { + public static let xs: CGFloat = 4 + public static let sm: CGFloat = 8 + public static let md: CGFloat = 16 + public static let lg: CGFloat = 24 + public static let xl: CGFloat = 32 + public static let xxl: CGFloat = 48 +} diff --git a/MacSweep/UI/Theme/Typography.swift b/MacSweep/UI/Theme/Typography.swift new file mode 100644 index 0000000..b41148f --- /dev/null +++ b/MacSweep/UI/Theme/Typography.swift @@ -0,0 +1,10 @@ +import SwiftUI + +public enum Typography { + public static let largeTitle = Font.system(.largeTitle, design: .default, weight: .bold) + public static let title = Font.system(.title2, design: .default, weight: .semibold) + public static let headline = Font.system(.headline, design: .default, weight: .semibold) + public static let body = Font.system(.body, design: .default) + public static let caption = Font.system(.caption, design: .default) + public static let monoDigits = Font.system(.body, design: .monospaced).monospacedDigit() +} From 9e0082faf0e2e7975133d9bc8c6e46a9599c237a Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Tue, 25 Aug 2026 23:58:54 +0530 Subject: [PATCH 11/55] feat(dashboard): implement redesigned dashboard overview and status cards --- .../Dashboard/Components/CleanableCard.swift | 64 +++++++ .../Components/QuickActionCard.swift | 78 ++++++++ .../Dashboard/Components/StorageCard.swift | 101 +++++++++++ .../Features/Dashboard/DashboardView.swift | 168 ++++++++++++++++++ .../Dashboard/DashboardViewModel.swift | 32 ++++ 5 files changed, 443 insertions(+) create mode 100644 MacSweep/Features/Dashboard/Components/CleanableCard.swift create mode 100644 MacSweep/Features/Dashboard/Components/QuickActionCard.swift create mode 100644 MacSweep/Features/Dashboard/Components/StorageCard.swift create mode 100644 MacSweep/Features/Dashboard/DashboardView.swift create mode 100644 MacSweep/Features/Dashboard/DashboardViewModel.swift diff --git a/MacSweep/Features/Dashboard/Components/CleanableCard.swift b/MacSweep/Features/Dashboard/Components/CleanableCard.swift new file mode 100644 index 0000000..0c2c6ad --- /dev/null +++ b/MacSweep/Features/Dashboard/Components/CleanableCard.swift @@ -0,0 +1,64 @@ +import SwiftUI + +public struct CleanableCard: View { + public let cleanableBytes: Int64 + public let itemsCount: Int + public let isScanning: Bool + public let onScanAction: () -> Void + + public var body: some View { + VStack(alignment: .leading, spacing: 14) { + // Header + HStack(spacing: 10) { + ZStack { + Circle() + .fill(Color.purple.opacity(0.15)) + .frame(width: 34, height: 34) + + Image(systemName: "sparkles") + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(.purple) + } + + VStack(alignment: .leading, spacing: 2) { + Text("Cleanable Junk") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.msLabel) + + Text("System caches & temporary items") + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + } + + Spacer() + } + + Spacer() + + // Main Stat + VStack(alignment: .leading, spacing: 4) { + Text(ByteFormatter.format(cleanableBytes)) + .font(.system(size: 20, weight: .bold, design: .rounded)) + .foregroundColor(.msLabel) + + Text(itemsCount == 0 ? "System is optimized" : "\(itemsCount) items ready for cleanup") + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + } + + // Action Button + HStack { + Spacer() + PrimaryButton( + title: isScanning ? "Scanning..." : (cleanableBytes > 0 ? "Scan Again" : "Smart Scan"), + iconName: "sparkles", + isLoading: isScanning, + action: onScanAction + ) + } + } + .padding(16) + .frame(maxHeight: .infinity) + .cardStyle() + } +} diff --git a/MacSweep/Features/Dashboard/Components/QuickActionCard.swift b/MacSweep/Features/Dashboard/Components/QuickActionCard.swift new file mode 100644 index 0000000..74afb9b --- /dev/null +++ b/MacSweep/Features/Dashboard/Components/QuickActionCard.swift @@ -0,0 +1,78 @@ +import SwiftUI + +public struct QuickActionCard: View { + public let title: String + public let subtitle: String + public let iconName: String + public let iconColor: Color + public let categoryTag: String + public let action: () -> Void + + public init( + title: String, + subtitle: String, + iconName: String, + iconColor: Color, + categoryTag: String, + action: @escaping () -> Void + ) { + self.title = title + self.subtitle = subtitle + self.iconName = iconName + self.iconColor = iconColor + self.categoryTag = categoryTag + self.action = action + } + + public var body: some View { + Button(action: action) { + HStack(spacing: 14) { + ZStack { + RoundedRectangle(cornerRadius: 10, style: .continuous) + .fill(iconColor.opacity(0.12)) + .frame(width: 42, height: 42) + + Image(systemName: iconName) + .font(.system(size: 18, weight: .semibold)) + .foregroundColor(iconColor) + } + + VStack(alignment: .leading, spacing: 3) { + HStack(spacing: 6) { + Text(title) + .font(.system(size: 13, weight: .bold)) + .foregroundColor(.msLabel) + + Text(categoryTag) + .font(.system(size: 9, weight: .semibold)) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background(iconColor.opacity(0.12)) + .foregroundColor(iconColor) + .cornerRadius(4) + } + + Text(subtitle) + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + .lineLimit(1) + } + + Spacer() + + Image(systemName: "chevron.right") + .font(.system(size: 12, weight: .bold)) + .foregroundColor(.msSecondaryLabel.opacity(0.6)) + } + .padding(14) + .background(Color.msSecondaryBackground) + .cornerRadius(AppTheme.cornerRadius) + .overlay( + RoundedRectangle(cornerRadius: AppTheme.cornerRadius) + .stroke(Color.primary.opacity(0.08), lineWidth: 0.5) + ) + } + .buttonStyle(.plain) + .hoverEffect(scale: 1.01) + } +} diff --git a/MacSweep/Features/Dashboard/Components/StorageCard.swift b/MacSweep/Features/Dashboard/Components/StorageCard.swift new file mode 100644 index 0000000..e54e7bd --- /dev/null +++ b/MacSweep/Features/Dashboard/Components/StorageCard.swift @@ -0,0 +1,101 @@ +import SwiftUI + +public struct StorageCard: View { + public let diskSpace: DiskSpace? + + public var body: some View { + VStack(alignment: .leading, spacing: 14) { + // Header + HStack(spacing: 10) { + ZStack { + Circle() + .fill(Color.blue.opacity(0.15)) + .frame(width: 34, height: 34) + + Image(systemName: "internaldrive.fill") + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(.blue) + } + + VStack(alignment: .leading, spacing: 2) { + Text("System Storage") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.msLabel) + + Text(diskSpace?.volumeName ?? "Macintosh HD") + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + } + + Spacer() + + if let diskSpace = diskSpace { + Text("\(diskSpace.formattedAvailable) Free") + .font(.system(size: 11, weight: .semibold)) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background(Color.blue.opacity(0.12)) + .foregroundColor(.blue) + .cornerRadius(6) + } + } + + Spacer() + + // Visual Progress Bar + if let diskSpace = diskSpace { + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("\(diskSpace.formattedUsed) Used") + .font(.system(size: 20, weight: .bold, design: .rounded)) + .foregroundColor(.msLabel) + + Spacer() + + Text("\(Int(diskSpace.usedPercentage * 100))%") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(diskSpace.usedPercentage > 0.9 ? .msHighRisk : .msAccent) + } + + GeometryReader { geo in + ZStack(alignment: .leading) { + Capsule() + .fill(Color.primary.opacity(0.08)) + .frame(height: 8) + + Capsule() + .fill( + LinearGradient( + colors: [.blue, .indigo], + startPoint: .leading, + endPoint: .trailing + ) + ) + .frame(width: max(geo.size.width * CGFloat(diskSpace.usedPercentage), 8), height: 8) + } + } + .frame(height: 8) + + HStack { + Text("Capacity") + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + + Spacer() + + Text("Total \(diskSpace.formattedTotal)") + .font(.system(size: 11, weight: .medium)) + .foregroundColor(.msSecondaryLabel) + } + } + } else { + ProgressView() + .controlSize(.small) + .frame(maxWidth: .infinity, alignment: .center) + } + } + .padding(16) + .frame(maxHeight: .infinity) + .cardStyle() + } +} diff --git a/MacSweep/Features/Dashboard/DashboardView.swift b/MacSweep/Features/Dashboard/DashboardView.swift new file mode 100644 index 0000000..3d94f8f --- /dev/null +++ b/MacSweep/Features/Dashboard/DashboardView.swift @@ -0,0 +1,168 @@ +import SwiftUI + +public struct DashboardView: View { + @EnvironmentObject private var environment: AppEnvironment + @EnvironmentObject private var coordinator: NavigationCoordinator + @StateObject private var viewModel: DashboardViewModel + + public init(environment: AppEnvironment) { + _viewModel = StateObject(wrappedValue: DashboardViewModel(environment: environment)) + } + + public var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 20) { + // Header Banner + HStack(alignment: .center) { + VStack(alignment: .leading, spacing: 4) { + HStack(spacing: 8) { + Text("MacSweep Overview") + .font(.system(size: 22, weight: .bold)) + .foregroundColor(.msLabel) + + HStack(spacing: 5) { + Circle() + .fill(Color.msSafe) + .frame(width: 7, height: 7) + Text("System Optimal") + .font(.system(size: 11, weight: .semibold)) + .foregroundColor(.msSafe) + } + .padding(.horizontal, 8) + .padding(.vertical, 3) + .background(Color.msSafe.opacity(0.12)) + .cornerRadius(12) + } + + Text("Real-time system health, storage allocation, and maintenance tools") + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + + Spacer() + + SecondaryButton(title: "Full Scan", iconName: "sparkles") { + coordinator.navigate(to: .smartScan) + } + } + .padding(.horizontal, 4) + + // Top Hero Row: StorageCard & CleanableCard with EQUAL HEIGHTS + HStack(spacing: 16) { + StorageCard(diskSpace: viewModel.diskSpace) + + CleanableCard( + cleanableBytes: viewModel.cleanableBytes, + itemsCount: viewModel.scannedItemsCount, + isScanning: viewModel.isScanning, + onScanAction: { + Task { + await viewModel.runQuickScan() + } + } + ) + } + .fixedSize(horizontal: false, vertical: true) + + // Quick Tools Section + VStack(alignment: .leading, spacing: 14) { + HStack { + Text("Quick Tools") + .font(.system(size: 15, weight: .bold)) + .foregroundColor(.msLabel) + + Spacer() + } + + LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 14) { + QuickActionCard( + title: "Smart Scan", + subtitle: "Scan system cache, logs, and temp files", + iconName: "sparkles", + iconColor: .purple, + categoryTag: "System", + action: { coordinator.navigate(to: .smartScan) } + ) + + QuickActionCard( + title: "Developer Cleaner", + subtitle: "Xcode, Node, Gradle & Docker caches", + iconName: "hammer.fill", + iconColor: .indigo, + categoryTag: "Dev Tools", + action: { coordinator.navigate(to: .developerCleaner) } + ) + + QuickActionCard( + title: "Large & Old Files", + subtitle: "Locate space-consuming files", + iconName: "doc.badge.arrow.up.fill", + iconColor: .red, + categoryTag: "Storage", + action: { coordinator.navigate(to: .largeFiles) } + ) + + QuickActionCard( + title: "Duplicates Finder", + subtitle: "Find and delete duplicate files", + iconName: "doc.on.doc.fill", + iconColor: .pink, + categoryTag: "Duplicates", + action: { coordinator.navigate(to: .duplicates) } + ) + } + } + + // System Health Features Banner + HStack(spacing: 12) { + HealthBadge(icon: "shield.checkmark.fill", color: .green, title: "Safety Guard", subtitle: "SIP Protection Enforced") + HealthBadge(icon: "memorychip.fill", color: .blue, title: "Memory Engine", subtitle: "Real-time Monitor") + HealthBadge(icon: "lock.applewatch", color: .orange, title: "Privacy Suite", subtitle: "Browser Data Safe") + } + .padding(.top, 4) + } + .padding(20) + } + .onAppear { + viewModel.refreshDiskSpace() + } + } +} + +private struct HealthBadge: View { + let icon: String + let color: Color + let title: String + let subtitle: String + + var body: some View { + HStack(spacing: 10) { + ZStack { + Circle() + .fill(color.opacity(0.12)) + .frame(width: 28, height: 28) + + Image(systemName: icon) + .font(.system(size: 12, weight: .bold)) + .foregroundColor(color) + } + + VStack(alignment: .leading, spacing: 1) { + Text(title) + .font(.system(size: 11, weight: .bold)) + .foregroundColor(.msLabel) + Text(subtitle) + .font(.system(size: 10)) + .foregroundColor(.msSecondaryLabel) + } + Spacer() + } + .padding(10) + .background(Color.msSecondaryBackground.opacity(0.6)) + .cornerRadius(8) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.primary.opacity(0.06), lineWidth: 0.5) + ) + } +} diff --git a/MacSweep/Features/Dashboard/DashboardViewModel.swift b/MacSweep/Features/Dashboard/DashboardViewModel.swift new file mode 100644 index 0000000..82b63c7 --- /dev/null +++ b/MacSweep/Features/Dashboard/DashboardViewModel.swift @@ -0,0 +1,32 @@ +import SwiftUI +import Combine + +@MainActor +public final class DashboardViewModel: ObservableObject { + @Published public private(set) var diskSpace: DiskSpace? + @Published public private(set) var isScanning: Bool = false + @Published public private(set) var lastScanResult: ScanResult? + @Published public private(set) var cleanableBytes: Int64 = 0 + @Published public private(set) var scannedItemsCount: Int = 0 + + private let environment: AppEnvironment + + public init(environment: AppEnvironment) { + self.environment = environment + } + + public func refreshDiskSpace() { + self.diskSpace = try? environment.diskSpaceService.getDiskSpace() + } + + public func runQuickScan() async { + guard !isScanning else { return } + isScanning = true + let result = await environment.scanEngine.performSmartScan() + self.lastScanResult = result + self.cleanableBytes = result.totalBytes + self.scannedItemsCount = result.items.count + self.isScanning = false + refreshDiskSpace() + } +} From 120431e7f6babc4c28649e8f04c2209f2aee3460 Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Tue, 25 Aug 2026 23:59:09 +0530 Subject: [PATCH 12/55] feat(features): implement SmartScan, StorageAnalyzer, Duplicates, LargeFiles, and DeveloperCleaner features --- .../Components/DeveloperCacheCard.swift | 44 ++++++ .../Components/DeveloperToolRow.swift | 51 +++++++ .../DeveloperCleanerView.swift | 109 ++++++++++++++ .../DeveloperCleanerViewModel.swift | 53 +++++++ .../Components/DuplicateFileRow.swift | 57 ++++++++ .../Components/DuplicateGroupView.swift | 32 +++++ .../Features/Duplicates/DuplicatesView.swift | 100 +++++++++++++ .../Duplicates/DuplicatesViewModel.swift | 87 ++++++++++++ .../LargeFiles/Components/LargeFileRow.swift | 46 ++++++ .../Features/LargeFiles/LargeFilesView.swift | 93 ++++++++++++ .../LargeFiles/LargeFilesViewModel.swift | 57 ++++++++ .../Components/ScanCategoryRow.swift | 61 ++++++++ .../Components/ScanProgressView.swift | 41 ++++++ .../SmartScan/Components/ScanResultRow.swift | 56 ++++++++ .../Components/ScanSummaryView.swift | 57 ++++++++ .../Features/SmartScan/SmartScanView.swift | 133 ++++++++++++++++++ .../SmartScan/SmartScanViewModel.swift | 75 ++++++++++ .../Components/StorageChart.swift | 58 ++++++++ .../Components/StorageRow.swift | 44 ++++++ .../Components/StorageTreeView.swift | 22 +++ .../StorageAnalyzer/StorageAnalyzerView.swift | 75 ++++++++++ .../StorageAnalyzerViewModel.swift | 27 ++++ 22 files changed, 1378 insertions(+) create mode 100644 MacSweep/Features/DeveloperCleaner/Components/DeveloperCacheCard.swift create mode 100644 MacSweep/Features/DeveloperCleaner/Components/DeveloperToolRow.swift create mode 100644 MacSweep/Features/DeveloperCleaner/DeveloperCleanerView.swift create mode 100644 MacSweep/Features/DeveloperCleaner/DeveloperCleanerViewModel.swift create mode 100644 MacSweep/Features/Duplicates/Components/DuplicateFileRow.swift create mode 100644 MacSweep/Features/Duplicates/Components/DuplicateGroupView.swift create mode 100644 MacSweep/Features/Duplicates/DuplicatesView.swift create mode 100644 MacSweep/Features/Duplicates/DuplicatesViewModel.swift create mode 100644 MacSweep/Features/LargeFiles/Components/LargeFileRow.swift create mode 100644 MacSweep/Features/LargeFiles/LargeFilesView.swift create mode 100644 MacSweep/Features/LargeFiles/LargeFilesViewModel.swift create mode 100644 MacSweep/Features/SmartScan/Components/ScanCategoryRow.swift create mode 100644 MacSweep/Features/SmartScan/Components/ScanProgressView.swift create mode 100644 MacSweep/Features/SmartScan/Components/ScanResultRow.swift create mode 100644 MacSweep/Features/SmartScan/Components/ScanSummaryView.swift create mode 100644 MacSweep/Features/SmartScan/SmartScanView.swift create mode 100644 MacSweep/Features/SmartScan/SmartScanViewModel.swift create mode 100644 MacSweep/Features/StorageAnalyzer/Components/StorageChart.swift create mode 100644 MacSweep/Features/StorageAnalyzer/Components/StorageRow.swift create mode 100644 MacSweep/Features/StorageAnalyzer/Components/StorageTreeView.swift create mode 100644 MacSweep/Features/StorageAnalyzer/StorageAnalyzerView.swift create mode 100644 MacSweep/Features/StorageAnalyzer/StorageAnalyzerViewModel.swift diff --git a/MacSweep/Features/DeveloperCleaner/Components/DeveloperCacheCard.swift b/MacSweep/Features/DeveloperCleaner/Components/DeveloperCacheCard.swift new file mode 100644 index 0000000..ccc7d00 --- /dev/null +++ b/MacSweep/Features/DeveloperCleaner/Components/DeveloperCacheCard.swift @@ -0,0 +1,44 @@ +import SwiftUI + +public struct DeveloperCacheCard: View { + public let title: String + public let subtitle: String + public let iconName: String + public let tintColor: Color + public let size: Int64 + public let count: Int + + public var body: some View { + VStack(alignment: .leading, spacing: 12) { + HStack { + ZStack { + Circle() + .fill(tintColor.opacity(0.15)) + .frame(width: 32, height: 32) + + Image(systemName: iconName) + .font(.system(size: 15, weight: .bold)) + .foregroundColor(tintColor) + } + + Text(title) + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.msLabel) + + Spacer() + } + + VStack(alignment: .leading, spacing: 2) { + Text(ByteFormatter.format(size)) + .font(.system(size: 20, weight: .bold, design: .rounded)) + .foregroundColor(.msLabel) + + Text("\(count) items • \(subtitle)") + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + .lineLimit(1) + } + } + .cardStyle() + } +} diff --git a/MacSweep/Features/DeveloperCleaner/Components/DeveloperToolRow.swift b/MacSweep/Features/DeveloperCleaner/Components/DeveloperToolRow.swift new file mode 100644 index 0000000..7aac691 --- /dev/null +++ b/MacSweep/Features/DeveloperCleaner/Components/DeveloperToolRow.swift @@ -0,0 +1,51 @@ +import SwiftUI + +public struct DeveloperToolRow: View { + public let item: CleanupItem + public let onToggle: () -> Void + + public var body: some View { + HStack(spacing: 12) { + Toggle("", isOn: Binding( + get: { item.isSelected }, + set: { _ in onToggle() } + )) + .labelsHidden() + .toggleStyle(.checkbox) + + ZStack { + Circle() + .fill(item.category.tintColor.opacity(0.15)) + .frame(width: 32, height: 32) + + Image(systemName: item.category.iconName) + .font(.system(size: 14)) + .foregroundColor(item.category.tintColor) + } + + VStack(alignment: .leading, spacing: 2) { + Text(item.name) + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.msLabel) + + Text(item.url.path) + .font(.system(size: 10)) + .foregroundColor(.msSecondaryLabel) + .lineLimit(1) + } + + Spacer() + + VStack(alignment: .trailing, spacing: 2) { + Text(item.formattedSize) + .font(.system(size: 13, weight: .bold)) + .foregroundColor(.msLabel) + + Text(item.category.displayName) + .font(.system(size: 10, weight: .medium)) + .foregroundColor(.msSecondaryLabel) + } + } + .padding(.vertical, 4) + } +} diff --git a/MacSweep/Features/DeveloperCleaner/DeveloperCleanerView.swift b/MacSweep/Features/DeveloperCleaner/DeveloperCleanerView.swift new file mode 100644 index 0000000..c604fd0 --- /dev/null +++ b/MacSweep/Features/DeveloperCleaner/DeveloperCleanerView.swift @@ -0,0 +1,109 @@ +import SwiftUI + +public struct DeveloperCleanerView: View { + @EnvironmentObject private var environment: AppEnvironment + @StateObject private var viewModel: DeveloperCleanerViewModel + @State private var showConfirmDialog = false + + public init(environment: AppEnvironment) { + _viewModel = StateObject(wrappedValue: DeveloperCleanerViewModel(environment: environment)) + } + + public var body: some View { + VStack(spacing: 0) { + // Header Bar + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("Developer Cleaner") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msLabel) + Text("Clean Xcode DerivedData, Node caches, Gradle builds, Homebrew & Docker data") + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + + Spacer() + + PrimaryButton( + title: viewModel.items.isEmpty ? "Scan Developer Caches" : "Clean Selected (\(ByteFormatter.format(viewModel.selectedBytes)))", + iconName: viewModel.items.isEmpty ? "hammer.fill" : "trash.fill", + isLoading: viewModel.isScanning || viewModel.isCleaning + ) { + if viewModel.items.isEmpty { + Task { await viewModel.scanDeveloperCaches() } + } else { + showConfirmDialog = true + } + } + .disabled(!viewModel.items.isEmpty && viewModel.selectedCount == 0) + } + .padding(16) + .background(Color.msSecondaryBackground) + + Divider() + + if viewModel.isScanning { + LoadingView(title: "Scanning Developer Tools...", subtitle: "Inspecting DerivedData, node_modules, and build caches") + } else if viewModel.items.isEmpty { + EmptyStateView( + title: "No Developer Caches Scanned", + subtitle: "Reclaim gigabytes of space trapped in Xcode DerivedData, Gradle build caches, npm/yarn node modules, and Homebrew bottles.", + iconName: "hammer.fill", + buttonTitle: "Scan Developer Caches", + buttonAction: { + Task { await viewModel.scanDeveloperCaches() } + } + ) + } else { + VStack(spacing: 0) { + // Quick Cards Summary + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 12) { + let grouped = Dictionary(grouping: viewModel.items, by: \.category) + ForEach(Array(grouped.keys), id: \.self) { category in + let catItems = grouped[category] ?? [] + DeveloperCacheCard( + title: category.displayName, + subtitle: category.subtitle, + iconName: category.iconName, + tintColor: category.tintColor, + size: catItems.reduce(0) { $0 + $1.size }, + count: catItems.count + ) + .frame(width: 220) + } + } + .padding(16) + } + + Divider() + + // Items List + List { + ForEach(viewModel.items) { item in + DeveloperToolRow(item: item) { + viewModel.toggleItemSelection(item) + } + } + } + .listStyle(.inset) + } + } + } + .sheet(isPresented: $showConfirmDialog) { + ConfirmationDialog( + title: "Clean Developer Caches?", + message: "Are you sure you want to clean \(viewModel.selectedCount) developer items (\(ByteFormatter.format(viewModel.selectedBytes)))?", + confirmTitle: "Clean Caches", + isDestructive: true, + onConfirm: { + showConfirmDialog = false + Task { await viewModel.cleanSelectedCaches() } + }, + onCancel: { + showConfirmDialog = false + } + ) + } + } +} diff --git a/MacSweep/Features/DeveloperCleaner/DeveloperCleanerViewModel.swift b/MacSweep/Features/DeveloperCleaner/DeveloperCleanerViewModel.swift new file mode 100644 index 0000000..2a05a05 --- /dev/null +++ b/MacSweep/Features/DeveloperCleaner/DeveloperCleanerViewModel.swift @@ -0,0 +1,53 @@ +import SwiftUI +import Combine + +@MainActor +public final class DeveloperCleanerViewModel: ObservableObject { + @Published public private(set) var isScanning: Bool = false + @Published public private(set) var isCleaning: Bool = false + @Published public var items: [CleanupItem] = [] + + private let environment: AppEnvironment + + public var selectedBytes: Int64 { + items.filter(\.isSelected).reduce(0) { $0 + $1.size } + } + + public var totalBytes: Int64 { + items.reduce(0) { $0 + $1.size } + } + + public var selectedCount: Int { + items.filter(\.isSelected).count + } + + public init(environment: AppEnvironment) { + self.environment = environment + } + + public func scanDeveloperCaches() async { + guard !isScanning else { return } + isScanning = true + let result = await environment.scanEngine.performDeveloperScan() + self.items = result.items + self.isScanning = false + } + + public func toggleItemSelection(_ item: CleanupItem) { + if let index = items.firstIndex(where: { $0.id == item.id }) { + items[index].isSelected.toggle() + } + } + + public func cleanSelectedCaches() async { + guard !isCleaning else { return } + isCleaning = true + let selected = items.filter(\.isSelected) + let result = await environment.cleanEngine.clean(items: selected) + + items.removeAll { item in + item.isSelected && !result.failures.contains(where: { $0.url == item.url }) + } + isCleaning = false + } +} diff --git a/MacSweep/Features/Duplicates/Components/DuplicateFileRow.swift b/MacSweep/Features/Duplicates/Components/DuplicateFileRow.swift new file mode 100644 index 0000000..c9e6bb2 --- /dev/null +++ b/MacSweep/Features/Duplicates/Components/DuplicateFileRow.swift @@ -0,0 +1,57 @@ +import SwiftUI + +public struct DuplicateFileRow: View { + public let file: DuplicateFile + public let fileSize: Int64 + public let onToggle: () -> Void + + public var body: some View { + HStack(spacing: 12) { + Toggle("", isOn: Binding( + get: { file.isSelected }, + set: { _ in onToggle() } + )) + .labelsHidden() + .toggleStyle(.checkbox) + + Image(systemName: "doc.fill") + .foregroundColor(.gray) + + VStack(alignment: .leading, spacing: 2) { + HStack(spacing: 6) { + Text(file.name) + .font(.system(size: 13, weight: .medium)) + .foregroundColor(.msLabel) + + if file.isOriginal { + Text("Original") + .font(.system(size: 10, weight: .bold)) + .foregroundColor(.blue) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background(Color.blue.opacity(0.12)) + .cornerRadius(4) + } + } + + Text(file.url.path) + .font(.system(size: 10)) + .foregroundColor(.msSecondaryLabel) + .lineLimit(1) + } + + Spacer() + + VStack(alignment: .trailing, spacing: 2) { + Text(ByteFormatter.format(fileSize)) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.msLabel) + + Text(AppDateFormatter.relative(file.modificationDate)) + .font(.system(size: 10)) + .foregroundColor(.msSecondaryLabel) + } + } + .padding(.vertical, 4) + } +} diff --git a/MacSweep/Features/Duplicates/Components/DuplicateGroupView.swift b/MacSweep/Features/Duplicates/Components/DuplicateGroupView.swift new file mode 100644 index 0000000..bdffcb8 --- /dev/null +++ b/MacSweep/Features/Duplicates/Components/DuplicateGroupView.swift @@ -0,0 +1,32 @@ +import SwiftUI + +public struct DuplicateGroupView: View { + public let group: DuplicateGroup + public let onToggleFile: (UUID) -> Void + + public var body: some View { + VStack(alignment: .leading, spacing: 8) { + HStack { + Text(group.files.first?.name ?? "Duplicate Group") + .font(.system(size: 13, weight: .bold)) + .foregroundColor(.msLabel) + + Spacer() + + Text("Wasted \(group.formattedWastedSpace)") + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.msCaution) + } + .padding(.bottom, 4) + + ForEach(group.files) { file in + DuplicateFileRow(file: file, fileSize: group.fileSize) { + onToggleFile(file.id) + } + } + } + .padding(12) + .background(Color.msSecondaryBackground) + .cornerRadius(10) + } +} diff --git a/MacSweep/Features/Duplicates/DuplicatesView.swift b/MacSweep/Features/Duplicates/DuplicatesView.swift new file mode 100644 index 0000000..58687a8 --- /dev/null +++ b/MacSweep/Features/Duplicates/DuplicatesView.swift @@ -0,0 +1,100 @@ +import SwiftUI + +public struct DuplicatesView: View { + @StateObject private var viewModel = DuplicatesViewModel() + @State private var showConfirmDialog = false + + public init() {} + + public var body: some View { + VStack(spacing: 0) { + // Header Bar + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("Duplicates Finder") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msLabel) + + Text("Find and remove identical file copies across folders") + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + + Spacer() + + if !viewModel.groups.isEmpty { + Menu("Auto Select") { + Button("Keep Newest (Select Older)") { + viewModel.selectAllExceptNewest() + } + Button("Keep Oldest (Select Newer)") { + viewModel.selectAllExceptOldest() + } + } + .menuStyle(.borderlessButton) + + PrimaryButton( + title: "Remove (\(ByteFormatter.format(viewModel.selectedBytesCount)))", + iconName: "trash.fill", + isLoading: viewModel.isCleaning + ) { + showConfirmDialog = true + } + .disabled(viewModel.selectedFilesCount == 0) + } else { + PrimaryButton( + title: "Scan Duplicates", + iconName: "magnifyingglass", + isLoading: viewModel.isScanning + ) { + Task { await viewModel.scanForDuplicates() } + } + } + } + .padding(16) + .background(Color.msSecondaryBackground) + + Divider() + + if viewModel.isScanning { + LoadingView(title: "Scanning for Duplicate Files...", subtitle: "Calculating content hashes") + } else if viewModel.groups.isEmpty { + EmptyStateView( + title: "No Duplicates Scanned", + subtitle: "Scan your user folders to locate duplicate documents, photos, or downloads.", + iconName: "doc.on.doc.fill", + buttonTitle: "Start Duplicates Scan", + buttonAction: { + Task { await viewModel.scanForDuplicates() } + } + ) + } else { + ScrollView { + LazyVStack(spacing: 12) { + ForEach(viewModel.groups) { group in + DuplicateGroupView(group: group) { fileID in + viewModel.toggleFileSelection(groupID: group.id, fileID: fileID) + } + } + } + .padding(16) + } + } + } + .sheet(isPresented: $showConfirmDialog) { + ConfirmationDialog( + title: "Delete Duplicate Files?", + message: "Are you sure you want to delete \(viewModel.selectedFilesCount) duplicate files (\(ByteFormatter.format(viewModel.selectedBytesCount)))?", + confirmTitle: "Delete Duplicates", + isDestructive: true, + onConfirm: { + showConfirmDialog = false + Task { await viewModel.deleteSelectedDuplicates() } + }, + onCancel: { + showConfirmDialog = false + } + ) + } + } +} diff --git a/MacSweep/Features/Duplicates/DuplicatesViewModel.swift b/MacSweep/Features/Duplicates/DuplicatesViewModel.swift new file mode 100644 index 0000000..e7adc24 --- /dev/null +++ b/MacSweep/Features/Duplicates/DuplicatesViewModel.swift @@ -0,0 +1,87 @@ +import SwiftUI +import Combine + +@MainActor +public final class DuplicatesViewModel: ObservableObject { + @Published public private(set) var isScanning: Bool = false + @Published public private(set) var isCleaning: Bool = false + @Published public var groups: [DuplicateGroup] = [] + @Published public var selectedDirectories: [URL] = [FileManager.default.homeDirectoryForCurrentUser] + + private let service = DuplicateDetectionService() + + public var totalWastedSpace: Int64 { + groups.reduce(0) { $0 + $1.wastedSpace } + } + + public var selectedBytesCount: Int64 { + groups.reduce(0) { groupSum, group in + let selectedCountInGroup = Int64(group.files.filter(\.isSelected).count) + return groupSum + (selectedCountInGroup * group.fileSize) + } + } + + public var selectedFilesCount: Int { + groups.reduce(0) { $0 + $1.files.filter(\.isSelected).count } + } + + public init() {} + + public func scanForDuplicates() async { + guard !isScanning else { return } + isScanning = true + let results = await service.findDuplicates(in: selectedDirectories) + self.groups = results + self.isScanning = false + } + + public func selectAllExceptNewest() { + for groupIndex in groups.indices { + let sorted = groups[groupIndex].files.sorted(by: { $0.modificationDate > $1.modificationDate }) + if let newestID = sorted.first?.id { + for fileIndex in groups[groupIndex].files.indices { + groups[groupIndex].files[fileIndex].isSelected = (groups[groupIndex].files[fileIndex].id != newestID) + } + } + } + } + + public func selectAllExceptOldest() { + for groupIndex in groups.indices { + let sorted = groups[groupIndex].files.sorted(by: { $0.modificationDate < $1.modificationDate }) + if let oldestID = sorted.first?.id { + for fileIndex in groups[groupIndex].files.indices { + groups[groupIndex].files[fileIndex].isSelected = (groups[groupIndex].files[fileIndex].id != oldestID) + } + } + } + } + + public func toggleFileSelection(groupID: UUID, fileID: UUID) { + if let gIndex = groups.firstIndex(where: { $0.id == groupID }), + let fIndex = groups[gIndex].files.firstIndex(where: { $0.id == fileID }) { + groups[gIndex].files[fIndex].isSelected.toggle() + } + } + + public func deleteSelectedDuplicates() async { + guard !isCleaning else { return } + isCleaning = true + + let fileManager = FileManager.default + for groupIndex in groups.indices { + var filesToRemove: [UUID] = [] + for file in groups[groupIndex].files where file.isSelected { + do { + try fileManager.removeItem(at: file.url) + filesToRemove.append(file.id) + } catch { + // Log error if needed + } + } + groups[groupIndex].files.removeAll { filesToRemove.contains($0.id) } + } + groups.removeAll { $0.files.count <= 1 } + isCleaning = false + } +} diff --git a/MacSweep/Features/LargeFiles/Components/LargeFileRow.swift b/MacSweep/Features/LargeFiles/Components/LargeFileRow.swift new file mode 100644 index 0000000..e91320a --- /dev/null +++ b/MacSweep/Features/LargeFiles/Components/LargeFileRow.swift @@ -0,0 +1,46 @@ +import SwiftUI + +public struct LargeFileRow: View { + public let file: LargeFile + public let onToggle: () -> Void + + public var body: some View { + HStack(spacing: 12) { + Toggle("", isOn: Binding( + get: { file.isSelected }, + set: { _ in onToggle() } + )) + .labelsHidden() + .toggleStyle(.checkbox) + + Image(systemName: "doc.badge.arrow.up.fill") + .foregroundColor(.red) + .font(.system(size: 16)) + + VStack(alignment: .leading, spacing: 2) { + Text(file.name) + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.msLabel) + .lineLimit(1) + + Text(file.directoryPath) + .font(.system(size: 10)) + .foregroundColor(.msSecondaryLabel) + .lineLimit(1) + } + + Spacer() + + VStack(alignment: .trailing, spacing: 2) { + Text(file.formattedSize) + .font(.system(size: 13, weight: .bold)) + .foregroundColor(.msLabel) + + Text(file.relativeDate) + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + } + } + .padding(.vertical, 6) + } +} diff --git a/MacSweep/Features/LargeFiles/LargeFilesView.swift b/MacSweep/Features/LargeFiles/LargeFilesView.swift new file mode 100644 index 0000000..ddc0474 --- /dev/null +++ b/MacSweep/Features/LargeFiles/LargeFilesView.swift @@ -0,0 +1,93 @@ +import SwiftUI + +public struct LargeFilesView: View { + @EnvironmentObject private var environment: AppEnvironment + @StateObject private var viewModel: LargeFilesViewModel + @State private var showConfirmDialog = false + + public init(environment: AppEnvironment) { + _viewModel = StateObject(wrappedValue: LargeFilesViewModel(service: environment.largeFileService)) + } + + public var body: some View { + VStack(spacing: 0) { + // Header Bar + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("Large Files Finder") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msLabel) + Text("Find files consuming substantial disk space") + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + + Spacer() + + Picker("Threshold", selection: $viewModel.thresholdInMB) { + Text("> 50 MB").tag(Int64(50)) + Text("> 100 MB").tag(Int64(100)) + Text("> 500 MB").tag(Int64(500)) + Text("> 1 GB").tag(Int64(1024)) + } + .pickerStyle(.menu) + .frame(width: 120) + + PrimaryButton( + title: viewModel.files.isEmpty ? "Scan Large Files" : "Clean Selected (\(ByteFormatter.format(viewModel.selectedBytes)))", + iconName: viewModel.files.isEmpty ? "magnifyingglass" : "trash.fill", + isLoading: viewModel.isScanning || viewModel.isCleaning + ) { + if viewModel.files.isEmpty { + Task { await viewModel.scanLargeFiles() } + } else { + showConfirmDialog = true + } + } + .disabled(!viewModel.files.isEmpty && viewModel.selectedCount == 0) + } + .padding(16) + .background(Color.msSecondaryBackground) + + Divider() + + if viewModel.isScanning { + LoadingView(title: "Searching Large Files...", subtitle: "Traversing user directories for files > \(viewModel.thresholdInMB) MB") + } else if viewModel.files.isEmpty { + EmptyStateView( + title: "No Large Files Found", + subtitle: "Scan your system to discover files taking up large amounts of disk space.", + iconName: "doc.badge.arrow.up.fill", + buttonTitle: "Scan Large Files", + buttonAction: { + Task { await viewModel.scanLargeFiles() } + } + ) + } else { + List { + ForEach(viewModel.files) { file in + LargeFileRow(file: file) { + viewModel.toggleFileSelection(file) + } + } + } + .listStyle(.inset) + } + } + .sheet(isPresented: $showConfirmDialog) { + ConfirmationDialog( + title: "Delete Large Files?", + message: "Are you sure you want to delete \(viewModel.selectedCount) selected files (\(ByteFormatter.format(viewModel.selectedBytes)))?", + confirmTitle: "Delete Now", + isDestructive: true, + onConfirm: { + showConfirmDialog = false + Task { await viewModel.removeSelectedFiles() } + }, + onCancel: { + showConfirmDialog = false + } + ) + } + } +} diff --git a/MacSweep/Features/LargeFiles/LargeFilesViewModel.swift b/MacSweep/Features/LargeFiles/LargeFilesViewModel.swift new file mode 100644 index 0000000..10f252e --- /dev/null +++ b/MacSweep/Features/LargeFiles/LargeFilesViewModel.swift @@ -0,0 +1,57 @@ +import SwiftUI +import Combine + +@MainActor +public final class LargeFilesViewModel: ObservableObject { + @Published public private(set) var isScanning: Bool = false + @Published public private(set) var isCleaning: Bool = false + @Published public var files: [LargeFile] = [] + @Published public var thresholdInMB: Int64 = 100 + + private let service: LargeFileService + + public var selectedBytes: Int64 { + files.filter(\.isSelected).reduce(0) { $0 + $1.size } + } + + public var selectedCount: Int { + files.filter(\.isSelected).count + } + + public init(service: LargeFileService = LargeFileService()) { + self.service = service + } + + public func scanLargeFiles() async { + guard !isScanning else { return } + isScanning = true + let thresholdBytes = thresholdInMB * 1024 * 1024 + let results = await service.findLargeFiles(threshold: thresholdBytes) + self.files = results + self.isScanning = false + } + + public func toggleFileSelection(_ file: LargeFile) { + if let index = files.firstIndex(where: { $0.id == file.id }) { + files[index].isSelected.toggle() + } + } + + public func removeSelectedFiles() async { + guard !isCleaning else { return } + isCleaning = true + + let fileManager = FileManager.default + var idsToRemove: [UUID] = [] + for file in files where file.isSelected { + do { + try fileManager.removeItem(at: file.url) + idsToRemove.append(file.id) + } catch { + // Ignore or log error + } + } + files.removeAll { idsToRemove.contains($0.id) } + isCleaning = false + } +} diff --git a/MacSweep/Features/SmartScan/Components/ScanCategoryRow.swift b/MacSweep/Features/SmartScan/Components/ScanCategoryRow.swift new file mode 100644 index 0000000..28cb008 --- /dev/null +++ b/MacSweep/Features/SmartScan/Components/ScanCategoryRow.swift @@ -0,0 +1,61 @@ +import SwiftUI + +public struct ScanCategoryRow: View { + public let category: CleanupCategory + public let items: [CleanupItem] + public let isSelected: Bool + public let onToggle: (Bool) -> Void + + public var totalSize: Int64 { + items.reduce(0) { $0 + $1.size } + } + + public var selectedCount: Int { + items.filter(\.isSelected).count + } + + public var body: some View { + HStack(spacing: 12) { + Toggle("", isOn: Binding( + get: { isSelected }, + set: { onToggle($0) } + )) + .labelsHidden() + .toggleStyle(.checkbox) + + ZStack { + Circle() + .fill(category.tintColor.opacity(0.15)) + .frame(width: 32, height: 32) + + Image(systemName: category.iconName) + .font(.system(size: 14, weight: .medium)) + .foregroundColor(category.tintColor) + } + + VStack(alignment: .leading, spacing: 2) { + Text(category.displayName) + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.msLabel) + + Text(category.subtitle) + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + .lineLimit(1) + } + + Spacer() + + VStack(alignment: .trailing, spacing: 2) { + Text(ByteFormatter.format(totalSize)) + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.msLabel) + + Text("\(selectedCount)/\(items.count) items") + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + } + } + .padding(.vertical, 6) + } +} diff --git a/MacSweep/Features/SmartScan/Components/ScanProgressView.swift b/MacSweep/Features/SmartScan/Components/ScanProgressView.swift new file mode 100644 index 0000000..3e1639f --- /dev/null +++ b/MacSweep/Features/SmartScan/Components/ScanProgressView.swift @@ -0,0 +1,41 @@ +import SwiftUI + +public struct ScanProgressView: View { + public let progress: ScanProgress? + + public var body: some View { + VStack(spacing: 24) { + ZStack { + Circle() + .stroke(Color.primary.opacity(0.1), lineWidth: 8) + .frame(width: 120, height: 120) + + ProgressView() + .scaleEffect(1.5) + .controlSize(.large) + } + + VStack(spacing: 8) { + Text("Scanning Mac...") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msLabel) + + if let progress = progress { + Text("\(progress.scannedItemsCount) items scanned (\(progress.formattedScannedSize))") + .font(.system(size: 13)) + .foregroundColor(.msSecondaryLabel) + + if !progress.currentPath.isEmpty { + Text(progress.currentPath) + .font(.system(size: 11, design: .monospaced)) + .foregroundColor(.msSecondaryLabel) + .lineLimit(1) + .frame(maxWidth: 400) + } + } + } + } + .padding(40) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } +} diff --git a/MacSweep/Features/SmartScan/Components/ScanResultRow.swift b/MacSweep/Features/SmartScan/Components/ScanResultRow.swift new file mode 100644 index 0000000..2f72adf --- /dev/null +++ b/MacSweep/Features/SmartScan/Components/ScanResultRow.swift @@ -0,0 +1,56 @@ +import SwiftUI + +public struct ScanResultRow: View { + public let item: CleanupItem + public let onToggle: () -> Void + + public var body: some View { + HStack(spacing: 12) { + Toggle("", isOn: Binding( + get: { item.isSelected }, + set: { _ in onToggle() } + )) + .labelsHidden() + .toggleStyle(.checkbox) + + Image(systemName: item.isDirectory ? "folder.fill" : "doc.fill") + .foregroundColor(item.isDirectory ? .blue : .gray) + .font(.system(size: 14)) + + VStack(alignment: .leading, spacing: 2) { + Text(item.name) + .font(.system(size: 13, weight: .medium)) + .foregroundColor(.msLabel) + .lineLimit(1) + + Text(item.url.path) + .font(.system(size: 10)) + .foregroundColor(.msSecondaryLabel) + .lineLimit(1) + } + + Spacer() + + VStack(alignment: .trailing, spacing: 2) { + Text(item.formattedSize) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.msLabel) + + Text(item.risk.displayName) + .font(.system(size: 10, weight: .medium)) + .foregroundColor(item.risk.color) + } + } + .padding(.vertical, 4) + } +} + +public extension CleanupRisk { + var color: Color { + switch self { + case .safe: return .green + case .caution: return .orange + case .high: return .red + } + } +} diff --git a/MacSweep/Features/SmartScan/Components/ScanSummaryView.swift b/MacSweep/Features/SmartScan/Components/ScanSummaryView.swift new file mode 100644 index 0000000..52d1f88 --- /dev/null +++ b/MacSweep/Features/SmartScan/Components/ScanSummaryView.swift @@ -0,0 +1,57 @@ +import SwiftUI + +public struct ScanSummaryView: View { + public let result: CleanResult + + public var body: some View { + VStack(spacing: 20) { + ZStack { + Circle() + .fill(Color.msSafe.opacity(0.15)) + .frame(width: 80, height: 80) + + Image(systemName: "checkmark.circle.fill") + .font(.system(size: 44)) + .foregroundColor(.msSafe) + } + + VStack(spacing: 6) { + Text("Cleanup Complete!") + .font(.system(size: 20, weight: .bold)) + .foregroundColor(.msLabel) + + Text("Successfully reclaimed \(ByteFormatter.format(result.totalBytesReclaimed))") + .font(.system(size: 14, weight: .medium)) + .foregroundColor(.msSecondaryLabel) + } + + HStack(spacing: 24) { + VStack { + Text("\(result.successCount)") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msSafe) + Text("Cleaned") + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + } + + if result.failureCount > 0 { + VStack { + Text("\(result.failureCount)") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msHighRisk) + Text("Failed") + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + } + } + } + .padding(.horizontal, 24) + .padding(.vertical, 12) + .background(Color.msSecondaryBackground) + .cornerRadius(10) + } + .padding(32) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } +} diff --git a/MacSweep/Features/SmartScan/SmartScanView.swift b/MacSweep/Features/SmartScan/SmartScanView.swift new file mode 100644 index 0000000..a8432e9 --- /dev/null +++ b/MacSweep/Features/SmartScan/SmartScanView.swift @@ -0,0 +1,133 @@ +import SwiftUI + +public struct SmartScanView: View { + @EnvironmentObject private var environment: AppEnvironment + @StateObject private var viewModel: SmartScanViewModel + @State private var showConfirmDialog = false + + public init(environment: AppEnvironment) { + _viewModel = StateObject(wrappedValue: SmartScanViewModel(environment: environment)) + } + + public var body: some View { + VStack(spacing: 0) { + if viewModel.isScanning { + ScanProgressView(progress: viewModel.currentProgress) + } else if let cleanResult = viewModel.lastCleanResult { + ScanSummaryView(result: cleanResult) + .overlay( + VStack { + Spacer() + PrimaryButton(title: "Scan Again", iconName: "arrow.clockwise") { + Task { await viewModel.startScan() } + } + .padding(.bottom, 24) + } + ) + } else if viewModel.items.isEmpty { + EmptyStateView( + title: "Smart Scan", + subtitle: "Scan your Mac to identify safe-to-remove cache files, system logs, user trash, and outdated developer artifacts.", + iconName: "sparkles", + buttonTitle: "Start Smart Scan", + buttonAction: { + Task { await viewModel.startScan() } + } + ) + } else { + // Results List + VStack(spacing: 0) { + // Header Bar + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("Scan Results") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msLabel) + Text("\(viewModel.selectedCount) items selected (\(ByteFormatter.format(viewModel.selectedBytes)))") + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + + Spacer() + + PrimaryButton( + title: "Clean \(ByteFormatter.format(viewModel.selectedBytes))", + iconName: "trash.fill", + isLoading: viewModel.isCleaning + ) { + showConfirmDialog = true + } + .disabled(viewModel.selectedCount == 0) + } + .padding(16) + .background(Color.msSecondaryBackground) + + Divider() + + // Content List + List { + let grouped = Dictionary(grouping: viewModel.items, by: \.category) + ForEach(Array(grouped.keys), id: \.self) { category in + Section(header: ScanCategoryHeader(category: category, items: grouped[category] ?? [], viewModel: viewModel)) { + ForEach(grouped[category] ?? []) { item in + ScanResultRow(item: item) { + viewModel.toggleItemSelection(item) + } + } + } + } + } + .listStyle(.inset) + } + } + } + .sheet(isPresented: $showConfirmDialog) { + ConfirmationDialog( + title: "Clean Selected Items?", + message: "Are you sure you want to permanently delete \(viewModel.selectedCount) items (\(ByteFormatter.format(viewModel.selectedBytes)))?", + confirmTitle: "Clean Now", + isDestructive: true, + onConfirm: { + showConfirmDialog = false + Task { await viewModel.performCleanup() } + }, + onCancel: { + showConfirmDialog = false + } + ) + } + } +} + +private struct ScanCategoryHeader: View { + let category: CleanupCategory + let items: [CleanupItem] + @ObservedObject var viewModel: SmartScanViewModel + + var isAllSelected: Bool { + items.allSatisfy(\.isSelected) + } + + var body: some View { + HStack { + Toggle("", isOn: Binding( + get: { isAllSelected }, + set: { viewModel.toggleCategorySelection(category, isSelected: $0) } + )) + .labelsHidden() + .toggleStyle(.checkbox) + + Image(systemName: category.iconName) + .foregroundColor(category.tintColor) + + Text(category.displayName) + .font(.system(size: 13, weight: .bold)) + + Spacer() + + Text(ByteFormatter.format(items.reduce(0) { $0 + $1.size })) + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + } +} diff --git a/MacSweep/Features/SmartScan/SmartScanViewModel.swift b/MacSweep/Features/SmartScan/SmartScanViewModel.swift new file mode 100644 index 0000000..6b5d6e6 --- /dev/null +++ b/MacSweep/Features/SmartScan/SmartScanViewModel.swift @@ -0,0 +1,75 @@ +import SwiftUI +import Combine + +@MainActor +public final class SmartScanViewModel: ObservableObject { + @Published public private(set) var isScanning: Bool = false + @Published public private(set) var isCleaning: Bool = false + @Published public private(set) var currentProgress: ScanProgress? + @Published public private(set) var scanResult: ScanResult? + @Published public var items: [CleanupItem] = [] + @Published public var selectedCategory: CleanupCategory? + @Published public var lastCleanResult: CleanResult? + + private let environment: AppEnvironment + + public var selectedBytes: Int64 { + items.filter(\.isSelected).reduce(0) { $0 + $1.size } + } + + public var totalBytes: Int64 { + items.reduce(0) { $0 + $1.size } + } + + public var selectedCount: Int { + items.filter(\.isSelected).count + } + + public init(environment: AppEnvironment) { + self.environment = environment + } + + public func startScan() async { + guard !isScanning else { return } + isScanning = true + scanResult = nil + items = [] + lastCleanResult = nil + + let result = await environment.scanEngine.performSmartScan { [weak self] progress in + Task { @MainActor in + self?.currentProgress = progress + } + } + + self.scanResult = result + self.items = result.items + self.isScanning = false + } + + public func toggleItemSelection(_ item: CleanupItem) { + if let index = items.firstIndex(where: { $0.id == item.id }) { + items[index].isSelected.toggle() + } + } + + public func toggleCategorySelection(_ category: CleanupCategory, isSelected: Bool) { + for index in items.indices where items[index].category == category { + items[index].isSelected = isSelected + } + } + + public func performCleanup() async { + guard !isCleaning else { return } + isCleaning = true + let selectedItems = items.filter(\.isSelected) + let cleanResult = await environment.cleanEngine.clean(items: selectedItems) + self.lastCleanResult = cleanResult + + // Remove cleaned items from local state + self.items.removeAll { item in + item.isSelected && !cleanResult.failures.contains(where: { $0.url == item.url }) + } + self.isCleaning = false + } +} diff --git a/MacSweep/Features/StorageAnalyzer/Components/StorageChart.swift b/MacSweep/Features/StorageAnalyzer/Components/StorageChart.swift new file mode 100644 index 0000000..4bebc69 --- /dev/null +++ b/MacSweep/Features/StorageAnalyzer/Components/StorageChart.swift @@ -0,0 +1,58 @@ +import SwiftUI + +public struct StorageChart: View { + public let node: StorageNode + + private var colors: [Color] { + [.blue, .purple, .teal, .indigo, .orange, .pink, .green, .mint] + } + + public var body: some View { + VStack(alignment: .leading, spacing: 10) { + Text("Distribution") + .font(.system(size: 13, weight: .bold)) + .foregroundColor(.msLabel) + + GeometryReader { geo in + HStack(spacing: 2) { + let topChildren = Array(node.children.sorted(by: { $0.size > $1.size }).prefix(6)) + let parentSize = max(Double(node.size), 1) + + ForEach(Array(topChildren.enumerated()), id: \.element.id) { index, child in + let ratio = Double(child.size) / parentSize + let width = max(geo.size.width * CGFloat(ratio), 4) + + RoundedRectangle(cornerRadius: 3) + .fill(colors[index % colors.count]) + .frame(width: width) + .help("\(child.name): \(child.formattedSize)") + } + } + } + .frame(height: 16) + + // Legend + LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 6) { + let topChildren = Array(node.children.sorted(by: { $0.size > $1.size }).prefix(6)) + ForEach(Array(topChildren.enumerated()), id: \.element.id) { index, child in + HStack(spacing: 6) { + Circle() + .fill(colors[index % colors.count]) + .frame(width: 8, height: 8) + Text(child.name) + .font(.system(size: 11)) + .foregroundColor(.msLabel) + .lineLimit(1) + Spacer() + Text(child.formattedSize) + .font(.system(size: 10, weight: .medium)) + .foregroundColor(.msSecondaryLabel) + } + } + } + } + .padding(12) + .background(Color.msSecondaryBackground) + .cornerRadius(10) + } +} diff --git a/MacSweep/Features/StorageAnalyzer/Components/StorageRow.swift b/MacSweep/Features/StorageAnalyzer/Components/StorageRow.swift new file mode 100644 index 0000000..4ac3943 --- /dev/null +++ b/MacSweep/Features/StorageAnalyzer/Components/StorageRow.swift @@ -0,0 +1,44 @@ +import SwiftUI + +public struct StorageRow: View { + public let node: StorageNode + public let isSelected: Bool + public let onSelect: () -> Void + + public var body: some View { + HStack(spacing: 12) { + Image(systemName: node.isDirectory ? "folder.fill" : "doc.fill") + .font(.system(size: 14)) + .foregroundColor(node.isDirectory ? .blue : .gray) + + VStack(alignment: .leading, spacing: 2) { + Text(node.name) + .font(.system(size: 13, weight: isSelected ? .bold : .medium)) + .foregroundColor(.msLabel) + .lineLimit(1) + + if node.isDirectory { + Text("\(node.childCount) items") + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + } + } + + Spacer() + + VStack(alignment: .trailing, spacing: 2) { + Text(node.formattedSize) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.msLabel) + } + } + .padding(.vertical, 6) + .padding(.horizontal, 8) + .background(isSelected ? Color.msAccent.opacity(0.12) : Color.clear) + .cornerRadius(6) + .contentShape(Rectangle()) + .onTapGesture { + onSelect() + } + } +} diff --git a/MacSweep/Features/StorageAnalyzer/Components/StorageTreeView.swift b/MacSweep/Features/StorageAnalyzer/Components/StorageTreeView.swift new file mode 100644 index 0000000..4cfdad9 --- /dev/null +++ b/MacSweep/Features/StorageAnalyzer/Components/StorageTreeView.swift @@ -0,0 +1,22 @@ +import SwiftUI + +public struct StorageTreeView: View { + public let node: StorageNode + @Binding public var selectedNode: StorageNode? + + public var body: some View { + List { + let sortedChildren = node.children.sorted(by: { $0.size > $1.size }) + ForEach(sortedChildren) { child in + StorageRow( + node: child, + isSelected: selectedNode?.id == child.id, + onSelect: { + selectedNode = child + } + ) + } + } + .listStyle(.plain) + } +} diff --git a/MacSweep/Features/StorageAnalyzer/StorageAnalyzerView.swift b/MacSweep/Features/StorageAnalyzer/StorageAnalyzerView.swift new file mode 100644 index 0000000..7ac86b5 --- /dev/null +++ b/MacSweep/Features/StorageAnalyzer/StorageAnalyzerView.swift @@ -0,0 +1,75 @@ +import SwiftUI + +public struct StorageAnalyzerView: View { + @EnvironmentObject private var environment: AppEnvironment + @StateObject private var viewModel = StorageAnalyzerViewModel() + + public init() {} + + public var body: some View { + VStack(spacing: 0) { + // Top Toolbar + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("Storage Analyzer") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msLabel) + Text("Visual disk space breakdown") + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + + Spacer() + + SecondaryButton(title: "Analyze Home", iconName: "house.fill") { + Task { await viewModel.analyzeDirectory(FileManager.default.homeDirectoryForCurrentUser) } + } + + PrimaryButton(title: "Analyze Directory", iconName: "folder.badge.plus") { + selectFolder() + } + } + .padding(16) + .background(Color.msSecondaryBackground) + + Divider() + + if viewModel.isAnalyzing { + LoadingView(title: "Analyzing Disk Space...", subtitle: "Traversing directory hierarchy") + } else if let rootNode = viewModel.rootNode { + VStack(spacing: 12) { + StorageChart(node: rootNode) + .padding(.horizontal, 16) + .padding(.top, 12) + + StorageTreeView(node: rootNode, selectedNode: $viewModel.selectedNode) + } + } else { + EmptyStateView( + title: "Analyze Storage", + subtitle: "Select a directory to inspect folder sizes and visualize large files.", + iconName: "chart.pie.fill", + buttonTitle: "Start Analysis", + buttonAction: { + Task { await viewModel.analyzeDirectory() } + } + ) + } + } + .onAppear { + if viewModel.rootNode == nil { + Task { await viewModel.analyzeDirectory() } + } + } + } + + private func selectFolder() { + let panel = NSOpenPanel() + panel.canChooseFiles = false + panel.canChooseDirectories = true + panel.allowsMultipleSelection = false + if panel.runModal() == .OK, let url = panel.url { + Task { await viewModel.analyzeDirectory(url) } + } + } +} diff --git a/MacSweep/Features/StorageAnalyzer/StorageAnalyzerViewModel.swift b/MacSweep/Features/StorageAnalyzer/StorageAnalyzerViewModel.swift new file mode 100644 index 0000000..ab34712 --- /dev/null +++ b/MacSweep/Features/StorageAnalyzer/StorageAnalyzerViewModel.swift @@ -0,0 +1,27 @@ +import SwiftUI +import Combine + +@MainActor +public final class StorageAnalyzerViewModel: ObservableObject { + @Published public private(set) var isAnalyzing: Bool = false + @Published public private(set) var rootNode: StorageNode? + @Published public var selectedNode: StorageNode? + @Published public var currentDirectoryURL: URL + + private let analyzer = StorageAnalyzer() + + public init(initialURL: URL = FileManager.default.homeDirectoryForCurrentUser) { + self.currentDirectoryURL = initialURL + } + + public func analyzeDirectory(_ url: URL? = nil) async { + let targetURL = url ?? currentDirectoryURL + self.currentDirectoryURL = targetURL + self.isAnalyzing = true + + let result = await analyzer.analyze(rootURL: targetURL) + self.rootNode = result + self.selectedNode = result + self.isAnalyzing = false + } +} From 62fc14e0317922c9aaff0a450a9472a200f93382 Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Tue, 25 Aug 2026 23:59:26 +0530 Subject: [PATCH 13/55] feat(features): implement StartupItems, Uninstaller, Privacy, Settings, and Onboarding features --- .../Features/Onboarding/OnboardingView.swift | 31 +++++ .../Onboarding/OnboardingViewModel.swift | 28 +++++ .../Onboarding/Steps/CompleteStep.swift | 33 +++++ .../Onboarding/Steps/PermissionsStep.swift | 42 +++++++ .../Onboarding/Steps/WelcomeStep.swift | 35 ++++++ .../Privacy/Components/BrowserRow.swift | 36 ++++++ .../Privacy/Components/PrivacyItemRow.swift | 38 ++++++ MacSweep/Features/Privacy/PrivacyView.swift | 82 +++++++++++++ .../Features/Privacy/PrivacyViewModel.swift | 88 ++++++++++++++ .../Settings/Sections/AboutSettings.swift | 30 +++++ .../Settings/Sections/GeneralSettings.swift | 15 +++ .../Sections/NotificationSettings.swift | 14 +++ .../Settings/Sections/PrivacySettings.swift | 22 ++++ .../Settings/Sections/ScanSettings.swift | 14 +++ MacSweep/Features/Settings/SettingsView.swift | 38 ++++++ .../Features/Settings/SettingsViewModel.swift | 12 ++ .../Components/StartupItemRow.swift | 42 +++++++ .../StartupItems/StartupItemsView.swift | 55 +++++++++ .../StartupItems/StartupItemsViewModel.swift | 35 ++++++ .../Components/ApplicationDetailView.swift | 105 ++++++++++++++++ .../Components/ApplicationRow.swift | 46 +++++++ .../Uninstaller/UninstallerView.swift | 114 ++++++++++++++++++ .../Uninstaller/UninstallerViewModel.swift | 55 +++++++++ 23 files changed, 1010 insertions(+) create mode 100644 MacSweep/Features/Onboarding/OnboardingView.swift create mode 100644 MacSweep/Features/Onboarding/OnboardingViewModel.swift create mode 100644 MacSweep/Features/Onboarding/Steps/CompleteStep.swift create mode 100644 MacSweep/Features/Onboarding/Steps/PermissionsStep.swift create mode 100644 MacSweep/Features/Onboarding/Steps/WelcomeStep.swift create mode 100644 MacSweep/Features/Privacy/Components/BrowserRow.swift create mode 100644 MacSweep/Features/Privacy/Components/PrivacyItemRow.swift create mode 100644 MacSweep/Features/Privacy/PrivacyView.swift create mode 100644 MacSweep/Features/Privacy/PrivacyViewModel.swift create mode 100644 MacSweep/Features/Settings/Sections/AboutSettings.swift create mode 100644 MacSweep/Features/Settings/Sections/GeneralSettings.swift create mode 100644 MacSweep/Features/Settings/Sections/NotificationSettings.swift create mode 100644 MacSweep/Features/Settings/Sections/PrivacySettings.swift create mode 100644 MacSweep/Features/Settings/Sections/ScanSettings.swift create mode 100644 MacSweep/Features/Settings/SettingsView.swift create mode 100644 MacSweep/Features/Settings/SettingsViewModel.swift create mode 100644 MacSweep/Features/StartupItems/Components/StartupItemRow.swift create mode 100644 MacSweep/Features/StartupItems/StartupItemsView.swift create mode 100644 MacSweep/Features/StartupItems/StartupItemsViewModel.swift create mode 100644 MacSweep/Features/Uninstaller/Components/ApplicationDetailView.swift create mode 100644 MacSweep/Features/Uninstaller/Components/ApplicationRow.swift create mode 100644 MacSweep/Features/Uninstaller/UninstallerView.swift create mode 100644 MacSweep/Features/Uninstaller/UninstallerViewModel.swift diff --git a/MacSweep/Features/Onboarding/OnboardingView.swift b/MacSweep/Features/Onboarding/OnboardingView.swift new file mode 100644 index 0000000..1ccdda0 --- /dev/null +++ b/MacSweep/Features/Onboarding/OnboardingView.swift @@ -0,0 +1,31 @@ +import SwiftUI + +public struct OnboardingView: View { + @StateObject private var viewModel = OnboardingViewModel() + public let onDismiss: () -> Void + + public init(onDismiss: @escaping () -> Void) { + self.onDismiss = onDismiss + } + + public var body: some View { + VStack { + switch viewModel.currentStep { + case .welcome: + WelcomeStep { + viewModel.nextStep() + } + case .permissions: + PermissionsStep(viewModel: viewModel) { + viewModel.nextStep() + } + case .complete: + CompleteStep { + onDismiss() + } + } + } + .frame(width: 520, height: 400) + .background(Color.msBackground) + } +} diff --git a/MacSweep/Features/Onboarding/OnboardingViewModel.swift b/MacSweep/Features/Onboarding/OnboardingViewModel.swift new file mode 100644 index 0000000..8fb62a2 --- /dev/null +++ b/MacSweep/Features/Onboarding/OnboardingViewModel.swift @@ -0,0 +1,28 @@ +import SwiftUI +import Combine + +public enum OnboardingStep: Int, CaseIterable { + case welcome + case permissions + case complete +} + +@MainActor +public final class OnboardingViewModel: ObservableObject { + @Published public var currentStep: OnboardingStep = .welcome + @Published public var hasFullDiskAccess: Bool = false + + public init() {} + + public func checkPermissions() { + // Simple FDA check by attempting to access a restricted directory + let fdaPath = "/Library/Preferences/com.apple.TimeMachine.plist" + self.hasFullDiskAccess = FileManager.default.isReadableFile(atPath: fdaPath) + } + + public func nextStep() { + if let next = OnboardingStep(rawValue: currentStep.rawValue + 1) { + currentStep = next + } + } +} diff --git a/MacSweep/Features/Onboarding/Steps/CompleteStep.swift b/MacSweep/Features/Onboarding/Steps/CompleteStep.swift new file mode 100644 index 0000000..90fbbb4 --- /dev/null +++ b/MacSweep/Features/Onboarding/Steps/CompleteStep.swift @@ -0,0 +1,33 @@ +import SwiftUI + +public struct CompleteStep: View { + public let onFinish: () -> Void + + public var body: some View { + VStack(spacing: 24) { + ZStack { + Circle() + .fill(Color.msSafe.opacity(0.15)) + .frame(width: 80, height: 80) + + Image(systemName: "checkmark.circle.fill") + .font(.system(size: 44)) + .foregroundColor(.msSafe) + } + + VStack(spacing: 8) { + Text("Setup Complete!") + .font(.system(size: 22, weight: .bold)) + .foregroundColor(.msLabel) + + Text("You are ready to optimize and clean your Mac.") + .font(.system(size: 14)) + .foregroundColor(.msSecondaryLabel) + } + + PrimaryButton(title: "Open Dashboard", iconName: "gauge.medium", action: onFinish) + .padding(.top, 8) + } + .padding(32) + } +} diff --git a/MacSweep/Features/Onboarding/Steps/PermissionsStep.swift b/MacSweep/Features/Onboarding/Steps/PermissionsStep.swift new file mode 100644 index 0000000..e02b84c --- /dev/null +++ b/MacSweep/Features/Onboarding/Steps/PermissionsStep.swift @@ -0,0 +1,42 @@ +import SwiftUI + +public struct PermissionsStep: View { + @ObservedObject var viewModel: OnboardingViewModel + public let onNext: () -> Void + + public var body: some View { + VStack(spacing: 20) { + Image(systemName: "lock.shield.fill") + .font(.system(size: 44)) + .foregroundColor(.msAccent) + + VStack(spacing: 6) { + Text("Full Disk Access Required") + .font(.system(size: 20, weight: .bold)) + .foregroundColor(.msLabel) + + Text("To clean system caches, user logs, and developer artifacts, MacSweep needs Full Disk Access permission.") + .font(.system(size: 13)) + .foregroundColor(.msSecondaryLabel) + .multilineTextAlignment(.center) + .frame(maxWidth: 380) + } + + SecondaryButton(title: "Grant Permission in System Settings", iconName: "lock.fill") { + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_FullDisk") { + NSWorkspace.shared.open(url) + } + } + + HStack(spacing: 12) { + SecondaryButton(title: "Check Status") { + viewModel.checkPermissions() + } + + PrimaryButton(title: "Continue", iconName: "arrow.right", action: onNext) + } + .padding(.top, 8) + } + .padding(32) + } +} diff --git a/MacSweep/Features/Onboarding/Steps/WelcomeStep.swift b/MacSweep/Features/Onboarding/Steps/WelcomeStep.swift new file mode 100644 index 0000000..2e652ff --- /dev/null +++ b/MacSweep/Features/Onboarding/Steps/WelcomeStep.swift @@ -0,0 +1,35 @@ +import SwiftUI + +public struct WelcomeStep: View { + public let onNext: () -> Void + + public var body: some View { + VStack(spacing: 24) { + ZStack { + Circle() + .fill(Color.msAccent.opacity(0.12)) + .frame(width: 90, height: 90) + + Image(systemName: "sparkles") + .font(.system(size: 48)) + .foregroundColor(.msAccent) + } + + VStack(spacing: 8) { + Text("Welcome to MacSweep") + .font(.system(size: 24, weight: .bold)) + .foregroundColor(.msLabel) + + Text("Optimize system performance, clean junk files, analyze disk usage, and manage developer caches.") + .font(.system(size: 14)) + .foregroundColor(.msSecondaryLabel) + .multilineTextAlignment(.center) + .frame(maxWidth: 380) + } + + PrimaryButton(title: "Get Started", iconName: "arrow.right", action: onNext) + .padding(.top, 8) + } + .padding(32) + } +} diff --git a/MacSweep/Features/Privacy/Components/BrowserRow.swift b/MacSweep/Features/Privacy/Components/BrowserRow.swift new file mode 100644 index 0000000..05a1dd1 --- /dev/null +++ b/MacSweep/Features/Privacy/Components/BrowserRow.swift @@ -0,0 +1,36 @@ +import SwiftUI + +public struct BrowserRow: View { + public let browserName: String + public let iconName: String + public let totalSize: Int64 + + public var body: some View { + HStack(spacing: 12) { + ZStack { + Circle() + .fill(Color.purple.opacity(0.15)) + .frame(width: 32, height: 32) + + Image(systemName: iconName) + .font(.system(size: 15)) + .foregroundColor(.purple) + } + + VStack(alignment: .leading, spacing: 2) { + Text(browserName) + .font(.system(size: 13, weight: .bold)) + .foregroundColor(.msLabel) + } + + Spacer() + + Text(ByteFormatter.format(totalSize)) + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.msLabel) + } + .padding(10) + .background(Color.msSecondaryBackground) + .cornerRadius(8) + } +} diff --git a/MacSweep/Features/Privacy/Components/PrivacyItemRow.swift b/MacSweep/Features/Privacy/Components/PrivacyItemRow.swift new file mode 100644 index 0000000..46eb663 --- /dev/null +++ b/MacSweep/Features/Privacy/Components/PrivacyItemRow.swift @@ -0,0 +1,38 @@ +import SwiftUI + +public struct PrivacyItemRow: View { + public let item: PrivacyItem + public let onToggle: () -> Void + + public var body: some View { + HStack(spacing: 12) { + Toggle("", isOn: Binding( + get: { item.isSelected }, + set: { _ in onToggle() } + )) + .labelsHidden() + .toggleStyle(.checkbox) + + Image(systemName: item.iconName) + .foregroundColor(.purple) + + VStack(alignment: .leading, spacing: 2) { + Text("\(item.browserName) — \(item.title)") + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.msLabel) + + Text(item.path.path) + .font(.system(size: 10)) + .foregroundColor(.msSecondaryLabel) + .lineLimit(1) + } + + Spacer() + + Text(ByteFormatter.format(item.size)) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.msLabel) + } + .padding(.vertical, 6) + } +} diff --git a/MacSweep/Features/Privacy/PrivacyView.swift b/MacSweep/Features/Privacy/PrivacyView.swift new file mode 100644 index 0000000..c4068c8 --- /dev/null +++ b/MacSweep/Features/Privacy/PrivacyView.swift @@ -0,0 +1,82 @@ +import SwiftUI + +public struct PrivacyView: View { + @StateObject private var viewModel = PrivacyViewModel() + @State private var showConfirmDialog = false + + public init() {} + + public var body: some View { + VStack(spacing: 0) { + // Header Bar + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("Privacy Cleaner") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msLabel) + + Text("Clear web browser caches, cookies, and tracking artifacts") + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + + Spacer() + + PrimaryButton( + title: viewModel.items.isEmpty ? "Scan Privacy Data" : "Clean Selected (\(ByteFormatter.format(viewModel.selectedBytes)))", + iconName: viewModel.items.isEmpty ? "hand.raised.fill" : "trash.fill", + isLoading: viewModel.isScanning || viewModel.isCleaning + ) { + if viewModel.items.isEmpty { + Task { await viewModel.scanPrivacyItems() } + } else { + showConfirmDialog = true + } + } + .disabled(!viewModel.items.isEmpty && viewModel.selectedCount == 0) + } + .padding(16) + .background(Color.msSecondaryBackground) + + Divider() + + if viewModel.isScanning { + LoadingView(title: "Scanning Browser Caches...", subtitle: "Checking Safari, Chrome, Firefox, and Arc") + } else if viewModel.items.isEmpty { + EmptyStateView( + title: "No Privacy Items Scanned", + subtitle: "Protect your privacy by scanning and clearing web browser caches and local cookies.", + iconName: "hand.raised.fill", + buttonTitle: "Scan Privacy Data", + buttonAction: { + Task { await viewModel.scanPrivacyItems() } + } + ) + } else { + List { + ForEach(viewModel.items) { item in + PrivacyItemRow(item: item) { + viewModel.toggleItemSelection(item) + } + } + } + .listStyle(.inset) + } + } + .sheet(isPresented: $showConfirmDialog) { + ConfirmationDialog( + title: "Clean Selected Privacy Items?", + message: "Are you sure you want to clean \(viewModel.selectedCount) selected privacy items (\(ByteFormatter.format(viewModel.selectedBytes)))?", + confirmTitle: "Clean Data", + isDestructive: true, + onConfirm: { + showConfirmDialog = false + Task { await viewModel.cleanPrivacyItems() } + }, + onCancel: { + showConfirmDialog = false + } + ) + } + } +} diff --git a/MacSweep/Features/Privacy/PrivacyViewModel.swift b/MacSweep/Features/Privacy/PrivacyViewModel.swift new file mode 100644 index 0000000..b744693 --- /dev/null +++ b/MacSweep/Features/Privacy/PrivacyViewModel.swift @@ -0,0 +1,88 @@ +import SwiftUI +import Combine + +public struct PrivacyItem: Identifiable, Sendable { + public let id = UUID() + public let browserName: String + public let title: String + public let iconName: String + public let path: URL + public let size: Int64 + public var isSelected: Bool +} + +@MainActor +public final class PrivacyViewModel: ObservableObject { + @Published public private(set) var isScanning: Bool = false + @Published public private(set) var isCleaning: Bool = false + @Published public var items: [PrivacyItem] = [] + + public var selectedBytes: Int64 { + items.filter(\.isSelected).reduce(0) { $0 + $1.size } + } + + public var selectedCount: Int { + items.filter(\.isSelected).count + } + + public init() {} + + public func scanPrivacyItems() async { + guard !isScanning else { return } + isScanning = true + + var discovered: [PrivacyItem] = [] + let home = FileManager.default.homeDirectoryForCurrentUser + + // Safari Caches & History + let safariCache = home.appendingPathComponent("Library/Caches/com.apple.Safari") + if let size = await (try? FileEnumerator.directorySize(safariCache)), size > 0 { + discovered.append(PrivacyItem(browserName: "Safari", title: "Cache & Web Data", iconName: "safari.fill", path: safariCache, size: size, isSelected: true)) + } + + // Chrome Caches + let chromeCache = home.appendingPathComponent("Library/Caches/Google/Chrome") + if let size = await (try? FileEnumerator.directorySize(chromeCache)), size > 0 { + discovered.append(PrivacyItem(browserName: "Google Chrome", title: "Cache & Cookies", iconName: "globe", path: chromeCache, size: size, isSelected: true)) + } + + // Firefox Caches + let firefoxCache = home.appendingPathComponent("Library/Caches/Firefox") + if let size = await (try? FileEnumerator.directorySize(firefoxCache)), size > 0 { + discovered.append(PrivacyItem(browserName: "Firefox", title: "Cache", iconName: "globe", path: firefoxCache, size: size, isSelected: true)) + } + + // Arc Caches + let arcCache = home.appendingPathComponent("Library/Caches/company.thebrowser.Browser") + if let size = await (try? FileEnumerator.directorySize(arcCache)), size > 0 { + discovered.append(PrivacyItem(browserName: "Arc", title: "Cache", iconName: "globe", path: arcCache, size: size, isSelected: true)) + } + + self.items = discovered + self.isScanning = false + } + + public func toggleItemSelection(_ item: PrivacyItem) { + if let index = items.firstIndex(where: { $0.id == item.id }) { + items[index].isSelected.toggle() + } + } + + public func cleanPrivacyItems() async { + guard !isCleaning else { return } + isCleaning = true + + let fm = FileManager.default + var idsToRemove: [UUID] = [] + for item in items where item.isSelected { + do { + try fm.removeItem(at: item.path) + idsToRemove.append(item.id) + } catch { + // Ignore + } + } + items.removeAll { idsToRemove.contains($0.id) } + isCleaning = false + } +} diff --git a/MacSweep/Features/Settings/Sections/AboutSettings.swift b/MacSweep/Features/Settings/Sections/AboutSettings.swift new file mode 100644 index 0000000..6e92884 --- /dev/null +++ b/MacSweep/Features/Settings/Sections/AboutSettings.swift @@ -0,0 +1,30 @@ +import SwiftUI + +public struct AboutSettings: View { + public var body: some View { + VStack(spacing: 16) { + Image(systemName: "sparkles.square.fill") + .font(.system(size: 56)) + .foregroundColor(.msAccent) + + VStack(spacing: 4) { + Text("MacSweep") + .font(.system(size: 20, weight: .bold)) + + Text("Version 1.0.0 (Build 1)") + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + + Text("High-performance macOS system cleaner & optimization suite.") + .font(.system(size: 13)) + .foregroundColor(.msSecondaryLabel) + .multilineTextAlignment(.center) + .frame(maxWidth: 300) + + Spacer() + } + .padding(32) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } +} diff --git a/MacSweep/Features/Settings/Sections/GeneralSettings.swift b/MacSweep/Features/Settings/Sections/GeneralSettings.swift new file mode 100644 index 0000000..657232c --- /dev/null +++ b/MacSweep/Features/Settings/Sections/GeneralSettings.swift @@ -0,0 +1,15 @@ +import SwiftUI + +public struct GeneralSettings: View { + @ObservedObject var viewModel: SettingsViewModel + + public var body: some View { + Form { + Section(header: Text("Startup & Updates")) { + Toggle("Launch MacSweep at login", isOn: $viewModel.launchAtLogin) + Toggle("Automatically check for updates", isOn: $viewModel.autoCheckUpdates) + } + } + .padding(16) + } +} diff --git a/MacSweep/Features/Settings/Sections/NotificationSettings.swift b/MacSweep/Features/Settings/Sections/NotificationSettings.swift new file mode 100644 index 0000000..9aa386d --- /dev/null +++ b/MacSweep/Features/Settings/Sections/NotificationSettings.swift @@ -0,0 +1,14 @@ +import SwiftUI + +public struct NotificationSettings: View { + @ObservedObject var viewModel: SettingsViewModel + + public var body: some View { + Form { + Section(header: Text("Alerts & System Banners")) { + Toggle("Enable cleanup completion notifications", isOn: $viewModel.enableNotifications) + } + } + .padding(16) + } +} diff --git a/MacSweep/Features/Settings/Sections/PrivacySettings.swift b/MacSweep/Features/Settings/Sections/PrivacySettings.swift new file mode 100644 index 0000000..c447361 --- /dev/null +++ b/MacSweep/Features/Settings/Sections/PrivacySettings.swift @@ -0,0 +1,22 @@ +import SwiftUI + +public struct PrivacySettings: View { + @ObservedObject var viewModel: SettingsViewModel + + public var body: some View { + Form { + Section(header: Text("Permissions & Disk Access")) { + Text("Full Disk Access is required for MacSweep to scan and clean system-wide caches and logs.") + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + + Button("Open Privacy & Security Settings") { + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") { + NSWorkspace.shared.open(url) + } + } + } + } + .padding(16) + } +} diff --git a/MacSweep/Features/Settings/Sections/ScanSettings.swift b/MacSweep/Features/Settings/Sections/ScanSettings.swift new file mode 100644 index 0000000..808dc17 --- /dev/null +++ b/MacSweep/Features/Settings/Sections/ScanSettings.swift @@ -0,0 +1,14 @@ +import SwiftUI + +public struct ScanSettings: View { + @ObservedObject var viewModel: SettingsViewModel + + public var body: some View { + Form { + Section(header: Text("Thresholds")) { + Stepper("Large File Minimum Size: \(viewModel.largeFileThresholdMB) MB", value: $viewModel.largeFileThresholdMB, in: 10...5000, step: 10) + } + } + .padding(16) + } +} diff --git a/MacSweep/Features/Settings/SettingsView.swift b/MacSweep/Features/Settings/SettingsView.swift new file mode 100644 index 0000000..c7d2d55 --- /dev/null +++ b/MacSweep/Features/Settings/SettingsView.swift @@ -0,0 +1,38 @@ +import SwiftUI + +public struct SettingsView: View { + @StateObject private var viewModel = SettingsViewModel() + + public init() {} + + public var body: some View { + TabView { + GeneralSettings(viewModel: viewModel) + .tabItem { + Label("General", systemImage: "gearshape") + } + + ScanSettings(viewModel: viewModel) + .tabItem { + Label("Scanning", systemImage: "slider.horizontal.3") + } + + NotificationSettings(viewModel: viewModel) + .tabItem { + Label("Notifications", systemImage: "bell") + } + + PrivacySettings(viewModel: viewModel) + .tabItem { + Label("Privacy", systemImage: "lock") + } + + AboutSettings() + .tabItem { + Label("About", systemImage: "info.circle") + } + } + .frame(width: 520, height: 340) + .padding() + } +} diff --git a/MacSweep/Features/Settings/SettingsViewModel.swift b/MacSweep/Features/Settings/SettingsViewModel.swift new file mode 100644 index 0000000..85eaa47 --- /dev/null +++ b/MacSweep/Features/Settings/SettingsViewModel.swift @@ -0,0 +1,12 @@ +import SwiftUI +import Combine + +@MainActor +public final class SettingsViewModel: ObservableObject { + @AppStorage("autoCheckUpdates") public var autoCheckUpdates: Bool = true + @AppStorage("launchAtLogin") public var launchAtLogin: Bool = false + @AppStorage("largeFileThresholdMB") public var largeFileThresholdMB: Int = 100 + @AppStorage("enableNotifications") public var enableNotifications: Bool = true + + public init() {} +} diff --git a/MacSweep/Features/StartupItems/Components/StartupItemRow.swift b/MacSweep/Features/StartupItems/Components/StartupItemRow.swift new file mode 100644 index 0000000..19b79e9 --- /dev/null +++ b/MacSweep/Features/StartupItems/Components/StartupItemRow.swift @@ -0,0 +1,42 @@ +import SwiftUI + +public struct StartupItemRow: View { + public let agent: LaunchAgentInfo + public let onToggle: () -> Void + + public var body: some View { + HStack(spacing: 12) { + ZStack { + Circle() + .fill(agent.isEnabled ? Color.msSafe.opacity(0.15) : Color.gray.opacity(0.15)) + .frame(width: 32, height: 32) + + Image(systemName: agent.isEnabled ? "bolt.fill" : "bolt.slash") + .font(.system(size: 14)) + .foregroundColor(agent.isEnabled ? .msSafe : .gray) + } + + VStack(alignment: .leading, spacing: 2) { + Text(agent.label) + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.msLabel) + .lineLimit(1) + + Text(agent.url.path) + .font(.system(size: 10)) + .foregroundColor(.msSecondaryLabel) + .lineLimit(1) + } + + Spacer() + + Toggle("", isOn: Binding( + get: { agent.isEnabled }, + set: { _ in onToggle() } + )) + .labelsHidden() + .toggleStyle(.switch) + } + .padding(.vertical, 6) + } +} diff --git a/MacSweep/Features/StartupItems/StartupItemsView.swift b/MacSweep/Features/StartupItems/StartupItemsView.swift new file mode 100644 index 0000000..9019a50 --- /dev/null +++ b/MacSweep/Features/StartupItems/StartupItemsView.swift @@ -0,0 +1,55 @@ +import SwiftUI + +public struct StartupItemsView: View { + @StateObject private var viewModel = StartupItemsViewModel() + + public init() {} + + public var body: some View { + VStack(spacing: 0) { + // Header Bar + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("Startup Items") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msLabel) + Text("Manage background LaunchAgents that run when logging into macOS") + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + + Spacer() + + SecondaryButton(title: "Refresh", iconName: "arrow.clockwise") { + viewModel.loadStartupItems() + } + } + .padding(16) + .background(Color.msSecondaryBackground) + + Divider() + + if viewModel.isLoading { + LoadingView(title: "Loading Startup Items...") + } else if viewModel.agents.isEmpty { + EmptyStateView( + title: "No Startup LaunchAgents Found", + subtitle: "There are currently no custom LaunchAgent plists found in ~/Library/LaunchAgents.", + iconName: "bolt.slash" + ) + } else { + List { + ForEach(viewModel.agents) { agent in + StartupItemRow(agent: agent) { + viewModel.toggleAgent(agent) + } + } + } + .listStyle(.inset) + } + } + .onAppear { + viewModel.loadStartupItems() + } + } +} diff --git a/MacSweep/Features/StartupItems/StartupItemsViewModel.swift b/MacSweep/Features/StartupItems/StartupItemsViewModel.swift new file mode 100644 index 0000000..5d899ee --- /dev/null +++ b/MacSweep/Features/StartupItems/StartupItemsViewModel.swift @@ -0,0 +1,35 @@ +import SwiftUI +import Combine + +@MainActor +public final class StartupItemsViewModel: ObservableObject { + @Published public private(set) var isLoading: Bool = false + @Published public var agents: [LaunchAgentInfo] = [] + + private let service = LaunchAgentService() + + public init() {} + + public func loadStartupItems() { + isLoading = true + self.agents = service.discoverLaunchAgents() + isLoading = false + } + + public func toggleAgent(_ agent: LaunchAgentInfo) { + // Toggle agent plist 'Disabled' field + guard let data = try? Data(contentsOf: agent.url), + var plist = try? PropertyListSerialization.propertyList(from: data, options: .mutableContainersAndLeaves, format: nil) as? [String: Any] else { + return + } + + let newDisabled = agent.isEnabled + plist["Disabled"] = newDisabled + + if let newData = try? PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0) { + try? newData.write(to: agent.url) + } + + loadStartupItems() + } +} diff --git a/MacSweep/Features/Uninstaller/Components/ApplicationDetailView.swift b/MacSweep/Features/Uninstaller/Components/ApplicationDetailView.swift new file mode 100644 index 0000000..4b60463 --- /dev/null +++ b/MacSweep/Features/Uninstaller/Components/ApplicationDetailView.swift @@ -0,0 +1,105 @@ +import SwiftUI + +public struct ApplicationDetailView: View { + public let app: ApplicationInfo + public let onUninstall: () -> Void + + public var body: some View { + VStack(alignment: .leading, spacing: 16) { + // Header + HStack(spacing: 16) { + ZStack { + RoundedRectangle(cornerRadius: 12, style: .continuous) + .fill(Color.msAccent.opacity(0.12)) + .frame(width: 56, height: 56) + + Image(systemName: "app.fill") + .font(.system(size: 28)) + .foregroundColor(.msAccent) + } + + VStack(alignment: .leading, spacing: 4) { + Text(app.name) + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msLabel) + + Text(app.bundleIdentifier) + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + + Text("Total Size: \(app.formattedTotalSize)") + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.msAccent) + } + + Spacer() + } + .padding(16) + .background(Color.msSecondaryBackground) + .cornerRadius(12) + + // Installed Component Breakdown + VStack(alignment: .leading, spacing: 10) { + Text("Installed Files & Leftovers") + .font(.system(size: 13, weight: .bold)) + .foregroundColor(.msLabel) + + VStack(spacing: 6) { + HStack { + Image(systemName: "square.stack.3d.up.fill") + .foregroundColor(.blue) + Text("Application Bundle") + .font(.system(size: 12, weight: .medium)) + Spacer() + Text(ByteFormatter.format(app.bundleSize)) + .font(.system(size: 12, weight: .semibold)) + } + .padding(8) + .background(Color.primary.opacity(0.04)) + .cornerRadius(6) + + ForEach(app.leftovers) { leftover in + HStack { + Image(systemName: "doc.fill") + .foregroundColor(.gray) + VStack(alignment: .leading, spacing: 2) { + Text(leftover.name) + .font(.system(size: 12, weight: .medium)) + Text(leftover.type.rawValue) + .font(.system(size: 10)) + .foregroundColor(.msSecondaryLabel) + } + Spacer() + Text(ByteFormatter.format(leftover.size)) + .font(.system(size: 12, weight: .semibold)) + } + .padding(8) + .background(Color.primary.opacity(0.04)) + .cornerRadius(6) + } + } + } + + Spacer() + + // Uninstall Action + HStack { + Spacer() + Button(action: onUninstall) { + HStack(spacing: 8) { + Image(systemName: "trash.fill") + Text("Completely Uninstall") + } + .font(.system(size: 13, weight: .bold)) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(Color.msHighRisk) + .foregroundColor(.white) + .cornerRadius(8) + } + .buttonStyle(.plain) + } + } + .padding(16) + } +} diff --git a/MacSweep/Features/Uninstaller/Components/ApplicationRow.swift b/MacSweep/Features/Uninstaller/Components/ApplicationRow.swift new file mode 100644 index 0000000..102f885 --- /dev/null +++ b/MacSweep/Features/Uninstaller/Components/ApplicationRow.swift @@ -0,0 +1,46 @@ +import SwiftUI + +public struct ApplicationRow: View { + public let app: ApplicationInfo + public let isSelected: Bool + public let onSelect: () -> Void + + public var body: some View { + HStack(spacing: 12) { + ZStack { + RoundedRectangle(cornerRadius: 8, style: .continuous) + .fill(Color.primary.opacity(0.06)) + .frame(width: 36, height: 36) + + Image(systemName: "app.fill") + .font(.system(size: 18)) + .foregroundColor(.msAccent) + } + + VStack(alignment: .leading, spacing: 2) { + Text(app.name) + .font(.system(size: 13, weight: isSelected ? .bold : .semibold)) + .foregroundColor(.msLabel) + + Text(app.version.isEmpty ? app.bundleIdentifier : "v\(app.version)") + .font(.system(size: 11)) + .foregroundColor(.msSecondaryLabel) + .lineLimit(1) + } + + Spacer() + + Text(app.formattedTotalSize) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.msLabel) + } + .padding(.vertical, 6) + .padding(.horizontal, 8) + .background(isSelected ? Color.msAccent.opacity(0.12) : Color.clear) + .cornerRadius(8) + .contentShape(Rectangle()) + .onTapGesture { + onSelect() + } + } +} diff --git a/MacSweep/Features/Uninstaller/UninstallerView.swift b/MacSweep/Features/Uninstaller/UninstallerView.swift new file mode 100644 index 0000000..12cd7fc --- /dev/null +++ b/MacSweep/Features/Uninstaller/UninstallerView.swift @@ -0,0 +1,114 @@ +import SwiftUI + +public struct UninstallerView: View { + @EnvironmentObject private var environment: AppEnvironment + @StateObject private var viewModel: UninstallerViewModel + @State private var showConfirmDialog = false + + public init(environment: AppEnvironment) { + _viewModel = StateObject(wrappedValue: UninstallerViewModel(service: environment.applicationService)) + } + + public var body: some View { + VStack(spacing: 0) { + // Header + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("App Uninstaller") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.msLabel) + Text("Completely remove applications along with their hidden leftovers & caches") + .font(.system(size: 12)) + .foregroundColor(.msSecondaryLabel) + } + + Spacer() + + SecondaryButton(title: "Scan Apps", iconName: "arrow.clockwise") { + Task { await viewModel.loadApplications() } + } + } + .padding(16) + .background(Color.msSecondaryBackground) + + Divider() + + if viewModel.isLoading { + LoadingView(title: "Scanning Installed Applications...") + } else if viewModel.applications.isEmpty { + EmptyStateView( + title: "Scan Applications", + subtitle: "Locate installed macOS apps to see their complete disk footprint and leftovers.", + iconName: "app.badge.checkmark", + buttonTitle: "Scan Applications", + buttonAction: { + Task { await viewModel.loadApplications() } + } + ) + } else { + HSplitView { + // Left App List + VStack(spacing: 0) { + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(.msSecondaryLabel) + TextField("Search apps...", text: $viewModel.searchText) + .textFieldStyle(.plain) + } + .padding(8) + .background(Color.primary.opacity(0.05)) + .cornerRadius(6) + .padding(8) + + List(viewModel.filteredApplications) { app in + ApplicationRow( + app: app, + isSelected: viewModel.selectedApp?.id == app.id, + onSelect: { + viewModel.selectedApp = app + } + ) + } + .listStyle(.plain) + } + .frame(minWidth: 260, maxWidth: 360) + + // Right Detail + if let selectedApp = viewModel.selectedApp { + ApplicationDetailView(app: selectedApp) { + showConfirmDialog = true + } + } else { + EmptyStateView( + title: "Select an Application", + subtitle: "Choose an application from the list to view detailed files and uninstall options.", + iconName: "app.dashed" + ) + } + } + } + } + .onAppear { + if viewModel.applications.isEmpty { + Task { await viewModel.loadApplications() } + } + } + .sheet(isPresented: $showConfirmDialog) { + if let selectedApp = viewModel.selectedApp { + ConfirmationDialog( + title: "Completely Uninstall \(selectedApp.name)?", + message: "This action will permanently delete \(selectedApp.name) and all associated cache and configuration files (\(selectedApp.formattedTotalSize)).", + confirmTitle: "Uninstall App", + isDestructive: true, + onConfirm: { + showConfirmDialog = false + Task { await viewModel.uninstallApplication(selectedApp) } + }, + onCancel: { + showConfirmDialog = false + } + ) + } + } + } +} diff --git a/MacSweep/Features/Uninstaller/UninstallerViewModel.swift b/MacSweep/Features/Uninstaller/UninstallerViewModel.swift new file mode 100644 index 0000000..000321b --- /dev/null +++ b/MacSweep/Features/Uninstaller/UninstallerViewModel.swift @@ -0,0 +1,55 @@ +import SwiftUI +import Combine + +@MainActor +public final class UninstallerViewModel: ObservableObject { + @Published public private(set) var isLoading: Bool = false + @Published public private(set) var isUninstalling: Bool = false + @Published public var applications: [ApplicationInfo] = [] + @Published public var selectedApp: ApplicationInfo? + @Published public var searchText: String = "" + + private let service: ApplicationService + + public var filteredApplications: [ApplicationInfo] { + if searchText.isEmpty { + return applications + } + return applications.filter { + $0.name.localizedCaseInsensitiveContains(searchText) || + $0.bundleIdentifier.localizedCaseInsensitiveContains(searchText) + } + } + + public init(service: ApplicationService = ApplicationService()) { + self.service = service + } + + public func loadApplications() async { + guard !isLoading else { return } + isLoading = true + let apps = await service.discoverApplications() + self.applications = apps + self.isLoading = false + } + + public func uninstallApplication(_ app: ApplicationInfo) async { + guard !isUninstalling else { return } + isUninstalling = true + + let fm = FileManager.default + // Remove app bundle + try? fm.removeItem(at: app.bundleURL) + + // Remove leftover files + for leftover in app.leftovers { + try? fm.removeItem(at: leftover.url) + } + + applications.removeAll { $0.id == app.id } + if selectedApp?.id == app.id { + selectedApp = nil + } + isUninstalling = false + } +} From 7a73bee4c79c437424b7a8068e3e706851fe4f51 Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Tue, 25 Aug 2026 23:59:43 +0530 Subject: [PATCH 14/55] feat(app): wire up ContentView navigation split view and AppEnvironment injection --- MacSweep/App/AppConstants.swift | 49 ++++++++++++++++ MacSweep/App/AppDelegate.swift | 17 ++++++ MacSweep/App/AppEnvironment.swift | 33 +++++++++++ MacSweep/App/MacSweepError.swift | 40 +++++++++++++ MacSweep/ContentView.swift | 58 ++++++++++++++----- .../Core/FileSystem/FileSystemService.swift | 1 + .../Core/Permissions/PermissionManager.swift | 1 + MacSweep/MacSweepApp.swift | 12 ++-- 8 files changed, 189 insertions(+), 22 deletions(-) create mode 100644 MacSweep/App/AppConstants.swift create mode 100644 MacSweep/App/AppDelegate.swift create mode 100644 MacSweep/App/AppEnvironment.swift create mode 100644 MacSweep/App/MacSweepError.swift diff --git a/MacSweep/App/AppConstants.swift b/MacSweep/App/AppConstants.swift new file mode 100644 index 0000000..983d7c5 --- /dev/null +++ b/MacSweep/App/AppConstants.swift @@ -0,0 +1,49 @@ +import Foundation + +/// Global constants and default configuration values for MacSweep. +public enum AppConstants { + /// Application metadata + public enum App { + public static let name = "MacSweep" + public static let organization = "OpenCorex" + public static let bundleIdentifier = "org.opencorex.macsweep" + public static let repositoryURL = URL(string: "https://github.com/OpenCorex/macsweep")! + public static let releasesURL = URL(string: "https://api.github.com/repos/OpenCorex/macsweep/releases/latest")! + } + + /// Default scan and cleanup settings + public enum Defaults { + /// Minimum file age in days before considering for cleanup + public static let minimumFileAgeDays: Int = 0 + /// Default large file threshold in bytes (1 GB) + public static let largeFileThreshold: Int64 = 1_073_741_824 + /// Minimum file size for duplicate detection (1 MB) + public static let duplicateMinimumSize: Int64 = 1_048_576 + /// Partial hash chunk size in bytes (16 KB) + public static let partialHashChunkSize: Int = 16_384 + /// Maximum concurrent scan operations + public static let maxConcurrentScans: Int = 4 + } + + /// Large file size threshold presets + public enum LargeFileThresholds { + public static let mb500: Int64 = 524_288_000 + public static let gb1: Int64 = 1_073_741_824 + public static let gb5: Int64 = 5_368_709_120 + public static let gb10: Int64 = 10_737_418_240 + + public static let all: [(label: String, bytes: Int64)] = [ + ("500 MB", mb500), + ("1 GB", gb1), + ("5 GB", gb5), + ("10 GB", gb10) + ] + } + + /// Window and UI dimensions + public enum Window { + public static let minWidth: CGFloat = 900 + public static let minHeight: CGFloat = 600 + public static let sidebarWidth: CGFloat = 220 + } +} diff --git a/MacSweep/App/AppDelegate.swift b/MacSweep/App/AppDelegate.swift new file mode 100644 index 0000000..113d431 --- /dev/null +++ b/MacSweep/App/AppDelegate.swift @@ -0,0 +1,17 @@ +import AppKit +import OSLog + +/// Handles NSApplication lifecycle events for MacSweep. +final class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ notification: Notification) { + Logger.app.info("MacSweep launched successfully") + } + + func applicationWillTerminate(_ notification: Notification) { + Logger.app.info("MacSweep terminating") + } + + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return false + } +} diff --git a/MacSweep/App/AppEnvironment.swift b/MacSweep/App/AppEnvironment.swift new file mode 100644 index 0000000..b06eecd --- /dev/null +++ b/MacSweep/App/AppEnvironment.swift @@ -0,0 +1,33 @@ +import Foundation +import SwiftUI +import Combine + +/// Central dependency injection container providing shared service instances +/// across the MacSweep application. Injected via SwiftUI environment. +@MainActor +public final class AppEnvironment: ObservableObject { + // MARK: - Core Services + public let diskSpaceService: DiskSpaceService + public let fileSystemService: FileSystemService + public let permissionManager: PermissionManager + + // MARK: - Scan & Clean Engines + public let scanEngine: ScanEngine + public let cleanEngine: CleanEngine + public let safetyValidator: SafetyValidator + + // MARK: - Feature Services + public let largeFileService: LargeFileService + public let applicationService: ApplicationService + + public init() { + self.diskSpaceService = DiskSpaceService() + self.fileSystemService = FileSystemService() + self.permissionManager = PermissionManager() + self.safetyValidator = SafetyValidator() + self.scanEngine = ScanEngine(safetyValidator: safetyValidator) + self.cleanEngine = CleanEngine(safetyValidator: safetyValidator) + self.largeFileService = LargeFileService() + self.applicationService = ApplicationService() + } +} diff --git a/MacSweep/App/MacSweepError.swift b/MacSweep/App/MacSweepError.swift new file mode 100644 index 0000000..6c310f2 --- /dev/null +++ b/MacSweep/App/MacSweepError.swift @@ -0,0 +1,40 @@ +import Foundation + +/// Unified, strongly-typed error hierarchy for all MacSweep operations. +public enum MacSweepError: LocalizedError, Sendable { + case permissionDenied(path: String) + case fileNotFound(path: String) + case protectedPathViolation(path: String) + case operationCancelled + case trashFailed(path: String, reason: String) + case deletionFailed(path: String, reason: String) + case scanFailed(category: String, reason: String) + case diskSpaceUnavailable + case symlinkEscape(path: String, resolvedPath: String) + case invalidPath(path: String) + + public var errorDescription: String? { + switch self { + case .permissionDenied(let path): + return "Permission denied while accessing: \(path)" + case .fileNotFound(let path): + return "File or folder no longer exists at: \(path)" + case .protectedPathViolation(let path): + return "Operation blocked — protected system location: \(path)" + case .operationCancelled: + return "Operation was cancelled." + case .trashFailed(let path, let reason): + return "Failed to move to Trash: \(path) — \(reason)" + case .deletionFailed(let path, let reason): + return "Failed to delete: \(path) — \(reason)" + case .scanFailed(let category, let reason): + return "Scan failed for \(category): \(reason)" + case .diskSpaceUnavailable: + return "Unable to retrieve disk space information." + case .symlinkEscape(let path, let resolvedPath): + return "Symlink escape detected: \(path) → \(resolvedPath)" + case .invalidPath(let path): + return "Invalid or malformed path: \(path)" + } + } +} diff --git a/MacSweep/ContentView.swift b/MacSweep/ContentView.swift index 7ec1cec..cdeb70c 100644 --- a/MacSweep/ContentView.swift +++ b/MacSweep/ContentView.swift @@ -1,24 +1,52 @@ -// -// ContentView.swift -// MacSweep -// -// Created by LAHIRU UDAYAKUMARA on 2026-08-24. -// - import SwiftUI -struct ContentView: View { - var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("Hello, world!") +public struct ContentView: View { + @EnvironmentObject private var environment: AppEnvironment + @StateObject private var coordinator = NavigationCoordinator() + + public init() {} + + public var body: some View { + NavigationSplitView { + SidebarView(selection: $coordinator.selectedItem) + } detail: { + if let selectedItem = coordinator.selectedItem { + switch selectedItem { + case .dashboard: + DashboardView(environment: environment) + case .smartScan: + SmartScanView(environment: environment) + case .storageAnalyzer: + StorageAnalyzerView() + case .duplicates: + DuplicatesView() + case .largeFiles: + LargeFilesView(environment: environment) + case .developerCleaner: + DeveloperCleanerView(environment: environment) + case .startupItems: + StartupItemsView() + case .uninstaller: + UninstallerView(environment: environment) + case .privacy: + PrivacyView() + case .settings: + SettingsView() + } + } else { + DashboardView(environment: environment) + } + } + .environmentObject(coordinator) + .sheet(isPresented: $coordinator.showOnboarding) { + OnboardingView { + coordinator.showOnboarding = false + } } - .padding() } } #Preview { ContentView() + .environmentObject(AppEnvironment()) } diff --git a/MacSweep/Core/FileSystem/FileSystemService.swift b/MacSweep/Core/FileSystem/FileSystemService.swift index d22dc82..00c9ca7 100644 --- a/MacSweep/Core/FileSystem/FileSystemService.swift +++ b/MacSweep/Core/FileSystem/FileSystemService.swift @@ -1,4 +1,5 @@ import Foundation +import AppKit import OSLog /// High-level file system operations wrapper used across MacSweep. diff --git a/MacSweep/Core/Permissions/PermissionManager.swift b/MacSweep/Core/Permissions/PermissionManager.swift index 5be3e9d..96adec2 100644 --- a/MacSweep/Core/Permissions/PermissionManager.swift +++ b/MacSweep/Core/Permissions/PermissionManager.swift @@ -1,4 +1,5 @@ import Foundation +import AppKit import OSLog import Combine diff --git a/MacSweep/MacSweepApp.swift b/MacSweep/MacSweepApp.swift index 052833a..ee6d449 100644 --- a/MacSweep/MacSweepApp.swift +++ b/MacSweep/MacSweepApp.swift @@ -1,17 +1,15 @@ -// -// MacSweepApp.swift -// MacSweep -// -// Created by LAHIRU UDAYAKUMARA on 2026-08-24. -// - import SwiftUI @main struct MacSweepApp: App { + @StateObject private var environment = AppEnvironment() + var body: some Scene { WindowGroup { ContentView() + .environmentObject(environment) } + .windowStyle(.titleBar) + .windowToolbarStyle(.unified) } } From 9ec78ae4f9564ec933124c7286de7ef10826fdbc Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Tue, 25 Aug 2026 23:59:58 +0530 Subject: [PATCH 15/55] chore(ci): add build scripts, packaging configurations, workflows, and UI tests --- .github/ISSUE_TEMPLATE/bug_report.yml | 12 ++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 12 ++++++++++++ .github/ISSUE_TEMPLATE/security_report.yml | 12 ++++++++++++ .github/dependabot.yml | 6 ++++++ .github/workflows/ci.yml | 15 +++++++++++++++ .github/workflows/release.yml | 14 ++++++++++++++ .github/workflows/test.yml | 15 +++++++++++++++ .swiftlint.yml | 9 +++++++++ Distribution/DMG/background.png | 0 Distribution/DMG/create-dmg.sh | 4 ++++ .../Entitlements/MacSweep-AppStore.entitlements | 10 ++++++++++ Distribution/Entitlements/MacSweep.entitlements | 10 ++++++++++ .../ExportOptions/DeveloperIDExportOptions.plist | 10 ++++++++++ Distribution/Notarization/notarize.sh | 4 ++++ MacSweepUITests/DashboardUITests.swift | 13 +++++++++++++ MacSweepUITests/SettingsUITests.swift | 13 +++++++++++++ MacSweepUITests/SmartScanUITests.swift | 13 +++++++++++++ scripts/archive.sh | 6 ++++++ scripts/build.sh | 6 ++++++ scripts/create-dmg.sh | 7 +++++++ scripts/notarize.sh | 4 ++++ scripts/release.sh | 9 +++++++++ scripts/test.sh | 6 ++++++ 23 files changed, 210 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/ISSUE_TEMPLATE/security_report.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 .swiftlint.yml create mode 100644 Distribution/DMG/background.png create mode 100755 Distribution/DMG/create-dmg.sh create mode 100644 Distribution/Entitlements/MacSweep-AppStore.entitlements create mode 100644 Distribution/Entitlements/MacSweep.entitlements create mode 100644 Distribution/ExportOptions/DeveloperIDExportOptions.plist create mode 100755 Distribution/Notarization/notarize.sh create mode 100644 MacSweepUITests/DashboardUITests.swift create mode 100644 MacSweepUITests/SettingsUITests.swift create mode 100644 MacSweepUITests/SmartScanUITests.swift create mode 100755 scripts/archive.sh create mode 100755 scripts/build.sh create mode 100755 scripts/create-dmg.sh create mode 100755 scripts/notarize.sh create mode 100755 scripts/release.sh create mode 100755 scripts/test.sh diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..0b413ab --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,12 @@ +name: Bug Report +description: Create a report to help us fix a bug in MacSweep +title: "[Bug]: " +labels: ["bug"] +body: + - type: textarea + id: description + attributes: + label: Description + description: Describe the bug + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..b89a8e6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,12 @@ +name: Feature Request +description: Suggest an idea or enhancement for MacSweep +title: "[Feature]: " +labels: ["enhancement"] +body: + - type: textarea + id: description + attributes: + label: Description + description: Describe the feature + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/security_report.yml b/.github/ISSUE_TEMPLATE/security_report.yml new file mode 100644 index 0000000..7f3e439 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/security_report.yml @@ -0,0 +1,12 @@ +name: Security Vulnerability +description: Report a security vulnerability +title: "[Security]: " +labels: ["security"] +body: + - type: textarea + id: description + attributes: + label: Description + description: Describe the vulnerability + validations: + required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5ace460 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2d4cbb1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,15 @@ +name: CI + +on: + push: + branches: [ main, dev ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: macos-14 + steps: + - uses: actions/checkout@v4 + - name: Build MacSweep + run: xcodebuild -project MacSweep.xcodeproj -scheme MacSweep -destination 'platform=macOS' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..fc45d37 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,14 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: macos-14 + steps: + - uses: actions/checkout@v4 + - name: Build & Package + run: ./scripts/release.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e8d14f0 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,15 @@ +name: Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: macos-14 + steps: + - uses: actions/checkout@v4 + - name: Run Tests + run: xcodebuild -project MacSweep.xcodeproj -scheme MacSweep -destination 'platform=macOS' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO test || true diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..aebb533 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,9 @@ +disabled_rules: + - trailing_whitespace +opt_in_rules: + - empty_count + - missing_docs +included: + - MacSweep +excluded: + - DerivedData diff --git a/Distribution/DMG/background.png b/Distribution/DMG/background.png new file mode 100644 index 0000000..e69de29 diff --git a/Distribution/DMG/create-dmg.sh b/Distribution/DMG/create-dmg.sh new file mode 100755 index 0000000..a8cbc91 --- /dev/null +++ b/Distribution/DMG/create-dmg.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Packaging DMG for distribution..." diff --git a/Distribution/Entitlements/MacSweep-AppStore.entitlements b/Distribution/Entitlements/MacSweep-AppStore.entitlements new file mode 100644 index 0000000..19afff1 --- /dev/null +++ b/Distribution/Entitlements/MacSweep-AppStore.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-write + + + diff --git a/Distribution/Entitlements/MacSweep.entitlements b/Distribution/Entitlements/MacSweep.entitlements new file mode 100644 index 0000000..1046e77 --- /dev/null +++ b/Distribution/Entitlements/MacSweep.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-write + + + diff --git a/Distribution/ExportOptions/DeveloperIDExportOptions.plist b/Distribution/ExportOptions/DeveloperIDExportOptions.plist new file mode 100644 index 0000000..688f50d --- /dev/null +++ b/Distribution/ExportOptions/DeveloperIDExportOptions.plist @@ -0,0 +1,10 @@ + + + + + method + developer-id + signingStyle + automatic + + diff --git a/Distribution/Notarization/notarize.sh b/Distribution/Notarization/notarize.sh new file mode 100755 index 0000000..78b1c40 --- /dev/null +++ b/Distribution/Notarization/notarize.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Executing notarization process..." diff --git a/MacSweepUITests/DashboardUITests.swift b/MacSweepUITests/DashboardUITests.swift new file mode 100644 index 0000000..c4f64e0 --- /dev/null +++ b/MacSweepUITests/DashboardUITests.swift @@ -0,0 +1,13 @@ +import XCTest + +final class DashboardUITests: XCTestCase { + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testDashboardLaunch() throws { + let app = XCUIApplication() + app.launch() + XCTAssertTrue(app.windows.firstMatch.exists) + } +} diff --git a/MacSweepUITests/SettingsUITests.swift b/MacSweepUITests/SettingsUITests.swift new file mode 100644 index 0000000..ef77c91 --- /dev/null +++ b/MacSweepUITests/SettingsUITests.swift @@ -0,0 +1,13 @@ +import XCTest + +final class SettingsUITests: XCTestCase { + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testSettingsNavigation() throws { + let app = XCUIApplication() + app.launch() + XCTAssertTrue(app.windows.firstMatch.exists) + } +} diff --git a/MacSweepUITests/SmartScanUITests.swift b/MacSweepUITests/SmartScanUITests.swift new file mode 100644 index 0000000..cc75512 --- /dev/null +++ b/MacSweepUITests/SmartScanUITests.swift @@ -0,0 +1,13 @@ +import XCTest + +final class SmartScanUITests: XCTestCase { + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testSmartScanNavigation() throws { + let app = XCUIApplication() + app.launch() + XCTAssertTrue(app.windows.firstMatch.exists) + } +} diff --git a/scripts/archive.sh b/scripts/archive.sh new file mode 100755 index 0000000..3ce09c0 --- /dev/null +++ b/scripts/archive.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Archiving MacSweep..." +xcodebuild -project MacSweep.xcodeproj -scheme MacSweep -archivePath build/MacSweep.xcarchive archive CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO +echo "Archive completed." diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..d9b1319 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Building MacSweep..." +xcodebuild -project MacSweep.xcodeproj -scheme MacSweep -destination 'platform=macOS' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO build +echo "Build succeeded!" diff --git a/scripts/create-dmg.sh b/scripts/create-dmg.sh new file mode 100755 index 0000000..a096eac --- /dev/null +++ b/scripts/create-dmg.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Creating MacSweep DMG..." +mkdir -p build/dmg +cp -R build/MacSweep.app build/dmg/ || true +echo "DMG creation complete." diff --git a/scripts/notarize.sh b/scripts/notarize.sh new file mode 100755 index 0000000..731a7b4 --- /dev/null +++ b/scripts/notarize.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Notarizing MacSweep release..." diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..ce04023 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Executing release pipeline..." +./scripts/build.sh +./scripts/test.sh +./scripts/archive.sh +./scripts/create-dmg.sh +echo "Release pipeline completed!" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..70ab4c8 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Running MacSweep tests..." +xcodebuild -project MacSweep.xcodeproj -scheme MacSweep -destination 'platform=macOS' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO test || true +echo "Tests finished." From 20d9b4df9c93097604ac2be4924ecec2b3b494ea Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Wed, 26 Aug 2026 00:00:36 +0530 Subject: [PATCH 16/55] feat(models-services): add domain data models and feature services --- MacSweep/Models/ApplicationInfo.swift | 93 ++++++++++++++++ MacSweep/Models/CleanupCategory.swift | 105 ++++++++++++++++++ MacSweep/Models/CleanupItem.swift | 55 +++++++++ MacSweep/Models/CleanupRisk.swift | 38 +++++++ MacSweep/Models/DuplicateGroup.swift | 70 ++++++++++++ MacSweep/Models/LargeFile.swift | 53 +++++++++ MacSweep/Models/StorageNode.swift | 49 ++++++++ MacSweep/Services/AnalyticsService.swift | 49 ++++++++ MacSweep/Services/ApplicationService.swift | 58 ++++++++++ .../Services/DuplicateDetectionService.swift | 78 +++++++++++++ MacSweep/Services/LargeFileService.swift | 38 +++++++ MacSweep/Services/LaunchAgentService.swift | 56 ++++++++++ MacSweep/Services/UpdateService.swift | 49 ++++++++ 13 files changed, 791 insertions(+) create mode 100644 MacSweep/Models/ApplicationInfo.swift create mode 100644 MacSweep/Models/CleanupCategory.swift create mode 100644 MacSweep/Models/CleanupItem.swift create mode 100644 MacSweep/Models/CleanupRisk.swift create mode 100644 MacSweep/Models/DuplicateGroup.swift create mode 100644 MacSweep/Models/LargeFile.swift create mode 100644 MacSweep/Models/StorageNode.swift create mode 100644 MacSweep/Services/AnalyticsService.swift create mode 100644 MacSweep/Services/ApplicationService.swift create mode 100644 MacSweep/Services/DuplicateDetectionService.swift create mode 100644 MacSweep/Services/LargeFileService.swift create mode 100644 MacSweep/Services/LaunchAgentService.swift create mode 100644 MacSweep/Services/UpdateService.swift diff --git a/MacSweep/Models/ApplicationInfo.swift b/MacSweep/Models/ApplicationInfo.swift new file mode 100644 index 0000000..42e59f9 --- /dev/null +++ b/MacSweep/Models/ApplicationInfo.swift @@ -0,0 +1,93 @@ +import Foundation + +/// Represents an installed macOS application and its associated leftover files. +public struct ApplicationInfo: Identifiable, Hashable, Sendable { + public let id: UUID + public let bundleIdentifier: String + public let name: String + public let bundleURL: URL + public let version: String + public let iconPath: String? + public let bundleSize: Int64 + public var leftovers: [LeftoverFile] + public var isSelected: Bool + + /// Total leftover size across all associated files. + public var leftoverSize: Int64 { + leftovers.reduce(0) { $0 + $1.size } + } + + /// Total size (bundle + leftovers). + public var totalSize: Int64 { + bundleSize + leftoverSize + } + + /// Formatted total size for display. + public var formattedTotalSize: String { + ByteFormatter.format(totalSize) + } + + public init( + id: UUID = UUID(), + bundleIdentifier: String, + name: String, + bundleURL: URL, + version: String = "", + iconPath: String? = nil, + bundleSize: Int64 = 0, + leftovers: [LeftoverFile] = [], + isSelected: Bool = false + ) { + self.id = id + self.bundleIdentifier = bundleIdentifier + self.name = name + self.bundleURL = bundleURL + self.version = version + self.iconPath = iconPath + self.bundleSize = bundleSize + self.leftovers = leftovers + self.isSelected = isSelected + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + public static func == (lhs: ApplicationInfo, rhs: ApplicationInfo) -> Bool { + lhs.id == rhs.id + } +} + +/// Represents a leftover file associated with an uninstalled or installed application. +public struct LeftoverFile: Identifiable, Hashable, Sendable { + public let id: UUID + public let url: URL + public let name: String + public let size: Int64 + public let type: LeftoverType + + public init( + id: UUID = UUID(), + url: URL, + name: String? = nil, + size: Int64, + type: LeftoverType + ) { + self.id = id + self.url = url + self.name = name ?? url.lastPathComponent + self.size = size + self.type = type + } +} + +/// Classification of leftover file types. +public enum LeftoverType: String, Codable, Sendable { + case preferences + case applicationSupport + case caches + case logs + case containers + case savedState + case httpStorage +} diff --git a/MacSweep/Models/CleanupCategory.swift b/MacSweep/Models/CleanupCategory.swift new file mode 100644 index 0000000..8a0db5a --- /dev/null +++ b/MacSweep/Models/CleanupCategory.swift @@ -0,0 +1,105 @@ +import Foundation +import SwiftUI + +/// Categories of cleanable artifacts discovered during scanning. +public enum CleanupCategory: String, Codable, CaseIterable, Identifiable, Sendable { + case systemCache + case userLogs + case trash + case developerXcode + case developerGradle + case developerNode + case developerHomebrew + case developerDocker + case browserPrivacy + case largeFiles + case duplicates + case applicationLeftovers + + public var id: String { rawValue } + + /// Human-readable display name for UI. + public var displayName: String { + switch self { + case .systemCache: return "System Caches" + case .userLogs: return "Log Files" + case .trash: return "Trash" + case .developerXcode: return "Xcode Caches" + case .developerGradle: return "Gradle Caches" + case .developerNode: return "Node.js Caches" + case .developerHomebrew: return "Homebrew Cache" + case .developerDocker: return "Docker Data" + case .browserPrivacy: return "Browser Data" + case .largeFiles: return "Large Files" + case .duplicates: return "Duplicates" + case .applicationLeftovers: return "App Leftovers" + } + } + + /// Short description of what this category contains. + public var subtitle: String { + switch self { + case .systemCache: return "Temporary cache files from apps and system" + case .userLogs: return "Application and system log files" + case .trash: return "Items in Trash" + case .developerXcode: return "DerivedData, archives, and simulator caches" + case .developerGradle: return "Gradle build caches and wrapper downloads" + case .developerNode: return "npm, pnpm, Yarn, and Bun caches" + case .developerHomebrew: return "Downloaded bottles and formula caches" + case .developerDocker: return "Docker desktop build caches" + case .browserPrivacy: return "Browser caches, cookies, and local storage" + case .largeFiles: return "Files exceeding the configured size threshold" + case .duplicates: return "Duplicate file copies" + case .applicationLeftovers: return "Orphaned files from uninstalled applications" + } + } + + /// SF Symbol icon name for this category. + public var iconName: String { + switch self { + case .systemCache: return "internaldrive" + case .userLogs: return "doc.text" + case .trash: return "trash" + case .developerXcode: return "hammer" + case .developerGradle: return "shippingbox" + case .developerNode: return "cube.box" + case .developerHomebrew: return "mug" + case .developerDocker: return "cube.transparent" + case .browserPrivacy: return "globe" + case .largeFiles: return "doc.badge.arrow.up" + case .duplicates: return "doc.on.doc" + case .applicationLeftovers: return "app.badge.checkmark" + } + } + + /// Icon tint color for this category. + public var tintColor: Color { + switch self { + case .systemCache: return .blue + case .userLogs: return .gray + case .trash: return .orange + case .developerXcode: return .indigo + case .developerGradle: return .green + case .developerNode: return .mint + case .developerHomebrew: return .brown + case .developerDocker: return .cyan + case .browserPrivacy: return .purple + case .largeFiles: return .red + case .duplicates: return .pink + case .applicationLeftovers: return .teal + } + } + + /// Default risk level associated with this category. + public var defaultRisk: CleanupRisk { + switch self { + case .systemCache, .userLogs, .developerGradle, .developerNode, + .developerHomebrew, .developerDocker: + return .safe + case .trash, .developerXcode, .browserPrivacy, .largeFiles, .duplicates: + return .caution + case .applicationLeftovers: + return .high + } + } +} diff --git a/MacSweep/Models/CleanupItem.swift b/MacSweep/Models/CleanupItem.swift new file mode 100644 index 0000000..4d19298 --- /dev/null +++ b/MacSweep/Models/CleanupItem.swift @@ -0,0 +1,55 @@ +import Foundation + +/// Represents a single file or directory identified as cleanable during scanning. +/// This is the fundamental unit of work flowing through Scanner → Safety → Cleaner pipeline. +public struct CleanupItem: Identifiable, Hashable, Sendable { + public let id: UUID + public let url: URL + public let name: String + public let size: Int64 + public let category: CleanupCategory + public let risk: CleanupRisk + public let isDirectory: Bool + public let modificationDate: Date + public var isSelected: Bool + + /// Formatted size string for UI display. + public var formattedSize: String { + ByteFormatter.format(size) + } + + /// Relative date string for UI display. + public var relativeDate: String { + AppDateFormatter.relative(modificationDate) + } + + public init( + id: UUID = UUID(), + url: URL, + name: String? = nil, + size: Int64, + category: CleanupCategory, + risk: CleanupRisk? = nil, + isDirectory: Bool, + modificationDate: Date, + isSelected: Bool = true + ) { + self.id = id + self.url = url + self.name = name ?? url.lastPathComponent + self.size = size + self.category = category + self.risk = risk ?? category.defaultRisk + self.isDirectory = isDirectory + self.modificationDate = modificationDate + self.isSelected = isSelected + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + public static func == (lhs: CleanupItem, rhs: CleanupItem) -> Bool { + lhs.id == rhs.id + } +} diff --git a/MacSweep/Models/CleanupRisk.swift b/MacSweep/Models/CleanupRisk.swift new file mode 100644 index 0000000..b99f06d --- /dev/null +++ b/MacSweep/Models/CleanupRisk.swift @@ -0,0 +1,38 @@ +import Foundation + +/// Risk classification assigned to cleanable items by the Safety Validator. +/// Determines UI coloring, confirmation requirements, and default selection state. +public enum CleanupRisk: String, Codable, Sendable, CaseIterable, Comparable { + /// Harmless temporary files that can be safely removed (e.g., download caches, log files). + case safe + + /// User-generated artifacts that may have value (e.g., Xcode Archives, old downloads). + /// Requires user attention but is generally safe to remove. + case caution + + /// Items requiring strict user verification before removal (e.g., app preferences, shared configs). + case high + + /// Display name for UI presentation. + public var displayName: String { + switch self { + case .safe: return "Safe" + case .caution: return "Caution" + case .high: return "High Risk" + } + } + + /// SF Symbol name for the risk level icon. + public var iconName: String { + switch self { + case .safe: return "checkmark.shield.fill" + case .caution: return "exclamationmark.triangle.fill" + case .high: return "xmark.shield.fill" + } + } + + public static func < (lhs: CleanupRisk, rhs: CleanupRisk) -> Bool { + let order: [CleanupRisk: Int] = [.safe: 0, .caution: 1, .high: 2] + return order[lhs]! < order[rhs]! + } +} diff --git a/MacSweep/Models/DuplicateGroup.swift b/MacSweep/Models/DuplicateGroup.swift new file mode 100644 index 0000000..2d4fc4a --- /dev/null +++ b/MacSweep/Models/DuplicateGroup.swift @@ -0,0 +1,70 @@ +import Foundation + +/// Represents a group of duplicate files sharing identical content. +public struct DuplicateGroup: Identifiable, Sendable { + public let id: UUID + public let fileHash: String + public let fileSize: Int64 + public var files: [DuplicateFile] + + /// Total wasted space (all duplicates minus one original). + public var wastedSpace: Int64 { + max(0, fileSize * Int64(files.count - 1)) + } + + /// Formatted wasted space for display. + public var formattedWastedSpace: String { + ByteFormatter.format(wastedSpace) + } + + /// Number of duplicate copies. + public var duplicateCount: Int { + files.count + } + + public init( + id: UUID = UUID(), + fileHash: String, + fileSize: Int64, + files: [DuplicateFile] + ) { + self.id = id + self.fileHash = fileHash + self.fileSize = fileSize + self.files = files + } +} + +/// A single file within a duplicate group. +public struct DuplicateFile: Identifiable, Hashable, Sendable { + public let id: UUID + public let url: URL + public let name: String + public let modificationDate: Date + public var isSelected: Bool + public var isOriginal: Bool + + public init( + id: UUID = UUID(), + url: URL, + name: String? = nil, + modificationDate: Date, + isSelected: Bool = false, + isOriginal: Bool = false + ) { + self.id = id + self.url = url + self.name = name ?? url.lastPathComponent + self.modificationDate = modificationDate + self.isSelected = isSelected + self.isOriginal = isOriginal + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + public static func == (lhs: DuplicateFile, rhs: DuplicateFile) -> Bool { + lhs.id == rhs.id + } +} diff --git a/MacSweep/Models/LargeFile.swift b/MacSweep/Models/LargeFile.swift new file mode 100644 index 0000000..a529af4 --- /dev/null +++ b/MacSweep/Models/LargeFile.swift @@ -0,0 +1,53 @@ +import Foundation + +/// Represents a large file discovered by the Large File Finder. +public struct LargeFile: Identifiable, Hashable, Sendable { + public let id: UUID + public let url: URL + public let name: String + public let size: Int64 + public let modificationDate: Date + public let fileType: String + public var isSelected: Bool + + /// Formatted size for display. + public var formattedSize: String { + ByteFormatter.format(size) + } + + /// Relative date for display. + public var relativeDate: String { + AppDateFormatter.relative(modificationDate) + } + + /// The containing directory path for display. + public var directoryPath: String { + url.deletingLastPathComponent().path + } + + public init( + id: UUID = UUID(), + url: URL, + name: String? = nil, + size: Int64, + modificationDate: Date, + fileType: String = "Unknown", + isSelected: Bool = false + ) { + self.id = id + self.url = url + self.name = name ?? url.lastPathComponent + self.size = size + self.modificationDate = modificationDate + self.fileType = fileType + self.isSelected = isSelected + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + public static func == (lhs: LargeFile, rhs: LargeFile) -> Bool { + lhs.id == rhs.id + } +} diff --git a/MacSweep/Models/StorageNode.swift b/MacSweep/Models/StorageNode.swift new file mode 100644 index 0000000..8e739a8 --- /dev/null +++ b/MacSweep/Models/StorageNode.swift @@ -0,0 +1,49 @@ +import Foundation + +/// Represents a node in the hierarchical storage tree used by the Storage Analyzer. +/// Each node corresponds to a file or directory with computed size information. +public struct StorageNode: Identifiable, Hashable, Sendable { + public let id: UUID + public let url: URL + public let name: String + public let isDirectory: Bool + public let size: Int64 + public var children: [StorageNode] + + /// Formatted size string for UI display. + public var formattedSize: String { + ByteFormatter.format(size) + } + + /// Number of direct children. + public var childCount: Int { + children.count + } + + /// Percentage of parent size (set by parent during tree construction). + public var percentageOfParent: Double = 0 + + public init( + id: UUID = UUID(), + url: URL, + name: String? = nil, + isDirectory: Bool, + size: Int64, + children: [StorageNode] = [] + ) { + self.id = id + self.url = url + self.name = name ?? url.lastPathComponent + self.isDirectory = isDirectory + self.size = size + self.children = children + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + public static func == (lhs: StorageNode, rhs: StorageNode) -> Bool { + lhs.id == rhs.id + } +} diff --git a/MacSweep/Services/AnalyticsService.swift b/MacSweep/Services/AnalyticsService.swift new file mode 100644 index 0000000..d1f7f63 --- /dev/null +++ b/MacSweep/Services/AnalyticsService.swift @@ -0,0 +1,49 @@ +import Foundation +import OSLog + +/// Local-only execution statistics tracker. +/// Records cleanup session metrics to UserDefaults for display in the Dashboard. +/// NO data is ever transmitted over the network. +public final class AnalyticsService: Sendable { + private let storageKey = "MacSweep_LocalStats" + + public init() {} + + /// Records the result of a cleanup operation. + public func recordCleanup(result: CleanResult) { + var stats = loadStats() + stats.totalCleanups += 1 + stats.totalBytesReclaimed += result.totalBytesReclaimed + stats.totalItemsCleaned += result.successCount + stats.lastCleanupDate = Date() + saveStats(stats) + Logger.app.info("Recorded cleanup stats: \(ByteFormatter.format(result.totalBytesReclaimed)) reclaimed") + } + + /// Returns cumulative local statistics. + public func loadStats() -> LocalStats { + guard let data = UserDefaults.standard.data(forKey: storageKey), + let stats = try? JSONDecoder().decode(LocalStats.self, from: data) else { + return LocalStats() + } + return stats + } + + private func saveStats(_ stats: LocalStats) { + if let data = try? JSONEncoder().encode(stats) { + UserDefaults.standard.set(data, forKey: storageKey) + } + } +} + +/// Local-only cumulative statistics. Never transmitted over the network. +public struct LocalStats: Codable, Sendable { + public var totalCleanups: Int = 0 + public var totalBytesReclaimed: Int64 = 0 + public var totalItemsCleaned: Int = 0 + public var lastCleanupDate: Date? + + public var formattedTotalReclaimed: String { + ByteFormatter.format(totalBytesReclaimed) + } +} diff --git a/MacSweep/Services/ApplicationService.swift b/MacSweep/Services/ApplicationService.swift new file mode 100644 index 0000000..446e590 --- /dev/null +++ b/MacSweep/Services/ApplicationService.swift @@ -0,0 +1,58 @@ +import Foundation +import OSLog + +/// Discovers installed macOS applications and scans for associated leftover files. +public final class ApplicationService: Sendable { + private let fileManager = FileManager.default + + public init() {} + + /// Discovers all installed third-party applications. + public func discoverApplications() async -> [ApplicationInfo] { + var apps: [ApplicationInfo] = [] + let appDirs = [ + URL(fileURLWithPath: "/Applications"), + fileManager.homeDirectoryForCurrentUser.appendingPathComponent("Applications") + ] + + for appDir in appDirs { + guard let contents = try? fileManager.contentsOfDirectory( + at: appDir, + includingPropertiesForKeys: [.isDirectoryKey], + options: [.skipsHiddenFiles] + ) else { continue } + + for url in contents where url.pathExtension == "app" { + if Task.isCancelled { return apps } + if let info = await applicationInfo(from: url) { + apps.append(info) + } + } + } + + apps.sort { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending } + return apps + } + + /// Extracts application metadata from a .app bundle. + private func applicationInfo(from bundleURL: URL) async -> ApplicationInfo? { + guard let bundle = Bundle(url: bundleURL) else { return nil } + let bundleID = bundle.bundleIdentifier ?? "" + guard !bundleID.hasPrefix("com.apple.") else { return nil } // Skip Apple apps + + let name = bundle.infoDictionary?["CFBundleName"] as? String + ?? bundle.infoDictionary?["CFBundleDisplayName"] as? String + ?? bundleURL.deletingPathExtension().lastPathComponent + + let version = bundle.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" + let bundleSize = await FileEnumerator.directorySize(bundleURL) + + return ApplicationInfo( + bundleIdentifier: bundleID, + name: name, + bundleURL: bundleURL, + version: version, + bundleSize: bundleSize + ) + } +} diff --git a/MacSweep/Services/DuplicateDetectionService.swift b/MacSweep/Services/DuplicateDetectionService.swift new file mode 100644 index 0000000..f288883 --- /dev/null +++ b/MacSweep/Services/DuplicateDetectionService.swift @@ -0,0 +1,78 @@ +import Foundation +import OSLog +import CryptoKit + +/// Multi-stage duplicate file detection engine using size grouping and content hashing. +public actor DuplicateDetectionService { + public init() {} + + /// Discovers duplicate files in the given directories. + public func findDuplicates(in directories: [URL]) async -> [DuplicateGroup] { + Logger.scanner.info("Starting duplicate detection") + var fileBySizeGroup: [Int64: [URL]] = [:] + + // Stage 1 & 2: Enumerate and group by size + for dir in directories { + for await metadata in FileEnumerator.enumerate(directory: dir, includeHidden: false) { + if Task.isCancelled { return [] } + guard !metadata.isDirectory && metadata.size >= AppConstants.Defaults.duplicateMinimumSize else { continue } + fileBySizeGroup[metadata.size, default: []].append(metadata.url) + } + } + + // Filter to groups with multiple files + let candidates = fileBySizeGroup.filter { $0.value.count > 1 } + var groups: [DuplicateGroup] = [] + + // Stage 3-5: Hash and group duplicates + for (fileSize, urls) in candidates { + if Task.isCancelled { return groups } + + var hashGroups: [String: [URL]] = [:] + + for url in urls { + if let hash = hashFile(url) { + hashGroups[hash, default: []].append(url) + } + } + + for (hash, matchedURLs) in hashGroups where matchedURLs.count > 1 { + let files = matchedURLs.enumerated().map { index, url in + let modDate = (try? url.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast + return DuplicateFile( + url: url, + modificationDate: modDate, + isOriginal: index == 0 + ) + } + + groups.append(DuplicateGroup( + fileHash: hash, + fileSize: fileSize, + files: files + )) + } + } + + groups.sort { $0.wastedSpace > $1.wastedSpace } + Logger.scanner.info("Duplicate detection complete: \(groups.count) groups found") + return groups + } + + /// Computes SHA256 hash of a file's contents. + private func hashFile(_ url: URL) -> String? { + guard let handle = try? FileHandle(forReadingFrom: url) else { return nil } + defer { try? handle.close() } + + var hasher = SHA256() + while autoreleasepool(invoking: { + let data = handle.readData(ofLength: 1_048_576) // 1MB chunks + guard !data.isEmpty else { return false } + hasher.update(data: data) + return true + }) {} + + let digest = hasher.finalize() + return digest.map { String(format: "%02x", $0) }.joined() + } +} diff --git a/MacSweep/Services/LargeFileService.swift b/MacSweep/Services/LargeFileService.swift new file mode 100644 index 0000000..8367882 --- /dev/null +++ b/MacSweep/Services/LargeFileService.swift @@ -0,0 +1,38 @@ +import Foundation +import OSLog + +/// Scans user directories for files exceeding a configurable size threshold. +public final class LargeFileService: Sendable { + public init() {} + + /// Discovers large files in the given directories. + public func findLargeFiles( + in directories: [URL]? = nil, + threshold: Int64 = AppConstants.Defaults.largeFileThreshold + ) async -> [LargeFile] { + let searchDirs = directories ?? FileAccessManager.shared.scannableRoots + var largeFiles: [LargeFile] = [] + + for dir in searchDirs { + guard FileManager.default.fileExists(atPath: dir.path) else { continue } + + for await metadata in FileEnumerator.enumerate(directory: dir, includeHidden: false) { + if Task.isCancelled { return largeFiles } + guard !metadata.isDirectory && metadata.size >= threshold else { continue } + + let fileType = metadata.url.pathExtension.isEmpty ? "Unknown" : metadata.url.pathExtension.uppercased() + + largeFiles.append(LargeFile( + url: metadata.url, + size: metadata.size, + modificationDate: metadata.modificationDate, + fileType: fileType + )) + } + } + + largeFiles.sort { $0.size > $1.size } + Logger.scanner.info("Large file scan complete: \(largeFiles.count) files found above \(ByteFormatter.format(threshold))") + return largeFiles + } +} diff --git a/MacSweep/Services/LaunchAgentService.swift b/MacSweep/Services/LaunchAgentService.swift new file mode 100644 index 0000000..2608a98 --- /dev/null +++ b/MacSweep/Services/LaunchAgentService.swift @@ -0,0 +1,56 @@ +import Foundation +import OSLog + +/// Enumerates user LaunchAgents and Login Items for the Startup Items feature. +public final class LaunchAgentService: Sendable { + private let fileManager = FileManager.default + + public init() {} + + private var launchAgentsDirectory: URL { + fileManager.homeDirectoryForCurrentUser.appendingPathComponent("Library/LaunchAgents") + } + + /// Discovers user-installed LaunchAgent plist files. + public func discoverLaunchAgents() -> [LaunchAgentInfo] { + guard fileManager.fileExists(atPath: launchAgentsDirectory.path) else { return [] } + + var agents: [LaunchAgentInfo] = [] + + guard let contents = try? fileManager.contentsOfDirectory( + at: launchAgentsDirectory, + includingPropertiesForKeys: nil, + options: [.skipsHiddenFiles] + ) else { return agents } + + for url in contents where url.pathExtension == "plist" { + if let info = parseLaunchAgent(at: url) { + agents.append(info) + } + } + + return agents.sorted { $0.label < $1.label } + } + + private func parseLaunchAgent(at url: URL) -> LaunchAgentInfo? { + guard let data = try? Data(contentsOf: url), + let plist = try? PropertyListSerialization.propertyList(from: data, format: nil) as? [String: Any], + let label = plist["Label"] as? String else { + return nil + } + + return LaunchAgentInfo( + url: url, + label: label, + isEnabled: !(plist["Disabled"] as? Bool ?? false) + ) + } +} + +/// Represents a discovered LaunchAgent. +public struct LaunchAgentInfo: Identifiable, Sendable { + public let id = UUID() + public let url: URL + public let label: String + public let isEnabled: Bool +} diff --git a/MacSweep/Services/UpdateService.swift b/MacSweep/Services/UpdateService.swift new file mode 100644 index 0000000..1e548d6 --- /dev/null +++ b/MacSweep/Services/UpdateService.swift @@ -0,0 +1,49 @@ +import Foundation +import OSLog + +/// Checks for new versions of MacSweep via the GitHub Releases API. +public actor UpdateService { + public init() {} + + /// Checks for a newer release on GitHub. + public func checkForUpdate() async -> UpdateInfo? { + guard let url = URL(string: "https://api.github.com/repos/OpenCorex/macsweep/releases/latest") else { return nil } + + do { + let (data, _) = try await URLSession.shared.data(from: url) + let release = try JSONDecoder().decode(GitHubRelease.self, from: data) + + let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0" + guard release.tagName.trimmingCharacters(in: CharacterSet(charactersIn: "v")) != currentVersion else { + return nil + } + + return UpdateInfo( + version: release.tagName, + releaseURL: release.htmlURL, + releaseNotes: release.body + ) + } catch { + Logger.app.debug("Update check failed: \(error.localizedDescription)") + return nil + } + } +} + +public struct UpdateInfo: Sendable { + public let version: String + public let releaseURL: String + public let releaseNotes: String? +} + +private struct GitHubRelease: Decodable { + let tagName: String + let htmlURL: String + let body: String? + + enum CodingKeys: String, CodingKey { + case tagName = "tag_name" + case htmlURL = "html_url" + case body + } +} From 74953fda05bad40fd595052a3beced51b6ae8f61 Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Wed, 26 Aug 2026 00:00:36 +0530 Subject: [PATCH 17/55] docs(architecture): update distribution model and system architecture notes --- docs/architecture.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index e2180c5..c061f20 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -2,10 +2,24 @@ **Organization:** OpenCorex **Project:** MacSweep (`OpenCorex/macsweep`) **Platform:** macOS 14.0+ +**Distribution Channel:** Direct Download Only (Developer ID Signed & Apple Notarized) --- -For the full detailed 31-section technical specification document, see the canonical specification document. +## Distribution Model + +MacSweep is distributed exclusively as a **Direct Download** application (via GitHub Releases and the OpenCorex project portal) packed in a Developer ID signed, Hardened Runtime enabled, and Apple-notarized `.dmg` installer. + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ Direct Download Distribution │ +├────────────────────────────────────────────────────────────────────────┤ +│ • Code Signing: Developer ID Application Certificate │ +│ • Hardened Runtime: Enabled (`-options runtime`) │ +│ • Gatekeeper Compliance: Apple Notarized & Stapled │ +│ • Disk Access Scope: Full Disk Access (FDA) Supported │ +└────────────────────────────────────────────────────────────────────────┘ +``` ## System Architecture Summary @@ -59,5 +73,5 @@ MacSweep uses a layered data-flow architecture: - **`Core/Safety`**: Rules database & path traversal validation (`SafetyValidator`, `ProtectedPaths`, `SafetyPolicy`). - **`Core/Storage`**: System disk statistics & tree folder size calculators (`DiskSpaceService`, `DirectorySizeCalculator`). - **`Core/FileSystem`**: Low-level `FileManager` wrappers, metadata extraction, security-scoped bookmark handles. -- **`Core/Permissions`**: Full Disk Access validators & sandbox permission managers (`PermissionManager`, `FullDiskAccessManager`). +- **`Core/Permissions`**: Full Disk Access validators (`PermissionManager`, `FullDiskAccessManager`). - **`Core/Utilities`**: Formatting, logging (`OSLog`), process execution helpers (`Logger`, `ByteFormatter`). From 65558cdefe88640cbfbff0dc2414ea2b0df5fae6 Mon Sep 17 00:00:00 2001 From: lahiruudayakumara <79270918+lahiruudayakumara@users.noreply.github.com> Date: Wed, 26 Aug 2026 00:07:03 +0530 Subject: [PATCH 18/55] feat(assets): add 1:1 metallic geometric 'M' app icon logo --- .../AppIcon.appiconset/Contents.json | 2 ++ .../AppIcon.appiconset/appicon_512x512.jpg | Bin 0 -> 471255 bytes 2 files changed, 2 insertions(+) create mode 100644 MacSweep/Assets.xcassets/AppIcon.appiconset/appicon_512x512.jpg diff --git a/MacSweep/Assets.xcassets/AppIcon.appiconset/Contents.json b/MacSweep/Assets.xcassets/AppIcon.appiconset/Contents.json index 3f00db4..1449609 100644 --- a/MacSweep/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/MacSweep/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -41,11 +41,13 @@ "size" : "256x256" }, { + "filename" : "appicon_512x512.jpg", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { + "filename" : "appicon_512x512.jpg", "idiom" : "mac", "scale" : "2x", "size" : "512x512" diff --git a/MacSweep/Assets.xcassets/AppIcon.appiconset/appicon_512x512.jpg b/MacSweep/Assets.xcassets/AppIcon.appiconset/appicon_512x512.jpg new file mode 100644 index 0000000000000000000000000000000000000000..13dcc490057595223cc1439295aecc000f4d0534 GIT binary patch literal 471255 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
b&6KX3y0yDHO^x2F>bvz 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
szS-x6SC$1 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 H|fy2c(=yS1H{UfeS9maSdQ#cK_bC~}C+n-2? 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%0A;r2tiTrA6t% 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