macOSアプリケーションの完全アンインストールと残留ファイルクリーンアップツール「Pearcleaner」の技術的解説

macOSにおけるアプリケーションアンインストールの課題

macOSでアプリケーションをゴミ箱に移動するだけでは、ディスク領域は完全には解放されません。アプリケーションの本体(.appバンドル)は削除されますが、システム全体に散らばる設定ファイル、キャッシュ、ログなどの「残留ファイル」が残存します。

これらの残留ファイルには以下のようなものが含まれます:

  • ユーザーライブラリ内の環境設定と構成ファイル(~/Library/Preferences)
  • アプリケーションサポートデータとプラグイン(~/Library/Application Support)
  • キャッシュと一時ファイル(~/Library/Caches)
  • システムログと診断データ

Pearcleanerは、これらの分散したファイルを特定し、安全に削除するために設計されたmacOS向けクリーンアップユーティリティです。スマートスキャンアルゴリズムを用いることで、アプリケーションの完全な退去処理を実現します。

Pearcleanerのスキャンアーキテクチャ

Pearcleanerは、アプリケーションの完全な削除を実現するために、3層のスキャンメカニズムを採用しています。Bundle Identifier(バンドルID)とファイルシステムの構造を解析し、関連ファイルを高精度で特定します。

1. 識別とメタデータ解析

  • Bundle IDマッピング: アプリケーションの一意な識別子(例: com.example.app)に基づき、関連ディレクトリを特定します。
  • ファイル構造解析: .appバンドル内部のInfo.plistやリソース構造を解析し、関連コンポーネントをマッピングします。
  • メタデータスキャン: ファイルシステムの拡張属性や作成日時を利用して、関連性の高いファイルを抽出します。

2. 深度検索アルゴリズム

  • マルチスレッド並列検索: GCD(Grand Central Dispatch)を活用し、システム全体の関連ディレクトリを高速にスキャンします。
  • ヒューリスティックマッチング: ファイル名の類似度、パスパターン、タイムスタンプを基に、バンドルIDに直接紐付かない孤立ファイルも検出します。

3. セーフティと検証

  • システム保護ホワイトリスト: macOSの_core_コンポーネントやシステムクリティカルなパスを自動的に除外します。
  • プレビューと検証: 削除実行前に、検出されたすべてのファイルパスとサイズをリスト化し、ユーザーによる最終確認を促します。

コア機能とシステム統合

Sentinelモードによるリアルタイム監視

Sentinelモードを有効にすると、アプリケーションがゴミ箱に移動されたイベントをフックし、自動的にクリーンアッププロセスを開始します。メモリフットプリントは約2MBに抑えられています。


// ファイルシステムイベントの監視と自動クリーンアップのトリガー
import Foundation

class TrashMonitor {
    private var eventStream: FSEventStreamRef?
    private let targetPath: String = NSHomeDirectory() + "/.Trash"
    
    func startObserving() {
        let context = FSEventStreamContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
        
        eventStream = FSEventStreamCreate(
            kCFAllocatorDefault,
            { (streamRef, clientCallBackInfo, numEvents, eventPaths, eventFlags, eventIds) in
                // ゴミ箱への変更イベントを検知し、解析キューに投入
                EventProcessor.shared.enqueueAnalysis(for: eventPaths)
            },
            &context,
            [targetPath] as CFArray,
            FSEventStreamEventId(kFSEventStreamEventIdSinceNow),
            1.0,
            FSEventStreamCreateFlags(kFSEventStreamCreateFlagFileEvents)
        )
        
        if let stream = eventStream {
            FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
            FSEventStreamStart(stream)
        }
    }
}
Pearcleaner UI

Homebrewエコシステムとの統合

パッケージマネージャであるHomebrewと深く統合されており、CaskやFormulaでインストールしたパッケージの依存関係や関連ファイルも一元管理できます。

インストールと権限設定

Homebrew経由でのインストール


brew install --cask pearcleaner

必要なシステム権限

ファイルシステムの深い階層にアクセスするため、初回起動時に以下の権限付与が必要です。

権限タイプ 必要性 技術的な目的
フルディスクアクセス 必須 ~/Library/内の保護されたディレクトリやMail/Calendarなどのサンドボックスデータへの読み取り
特権アシスタント 必須 /Library/ 配下のシステムレベルのディレクトリや所有権がrootのファイル削除
アクセシビリティ 任意 Finderからのドラッグ&ドロップ操作やUI自動化のサポート

ユースケースと自動化スクリプト

開発環境の定期的なクリーンアップ

開発者が複数のツールチェーンを頻繁に切り替える場合、キャッシュや古い設定ファイルが蓄積されがちです。以下のスクリプトは、特定の開発ツールに関連する孤立したファイルを安全に削除します。


// 開発ツール関連の孤立ファイルのクリーンアップロジック
func purgeOrphanedDevArtifacts(toolchain: String) {
    let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
    let supportDir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
    
    let searchPaths = [cacheDir, supportDir]
    let fileManager = FileManager.default
    
    for path in searchPaths {
        guard let contents = try? fileManager.contentsOfDirectory(at: path, includingPropertiesForKeys: nil) else { continue }
        
        for item in contents {
            // ツールチェーン名を含むが、現在インストールされていないアプリのディレクトリを特定
            if item.lastPathComponent.lowercased().contains(toolchain.lowercased()) && !isAppInstalled(toolchain) {
                try? fileManager.trashItem(at: item, resultingItemURL: nil)
                print("Removed orphaned artifact: \(item.lastPathComponent)")
            }
        }
    }
}

エンタープライズ環境でのバッチ処理

IT管理者が複数のMacに対して一括でアプリケーションの退去処理を行う場合、CLIとSSHを組み合わせた自動化が有効です。


#!/bin/bash
# 複数ノードに対する一括アンインストールとログ収集
NODES=("mac-node-01" "mac-node-02" "mac-node-03")
APP_BUNDLE_ID="com.enterprise.legacyapp"

for node in "${NODES[@]}"; do
    echo "Processing $node..."
    ssh "$node" << EOF
        # アプリケーションと関連ファイルのスキャンおよび削除
        /usr/local/bin/pear purge --bundle-id "$APP_BUNDLE_ID" --force --silent
        # 結果をsyslogに記録
        logger -t "Pearcleaner" "Successfully purged $APP_BUNDLE_ID"
EOF
done

技術スタックとセキュリティ設計

PearcleanerはモダンなmacOS開発スタックを採用しており、パフォーマンスとセキュリティを両立しています。

コンポーネント 技術スタック 役割
ユーザーインターフェース SwiftUI レスポンシブでネイティブなUI描画と状態管理
コアロジック Swift + Foundation ファイルシステム操作、バンドルID解析、非同期タスク処理
システム統合 AppKit + CoreServices Finder拡張機能、FSEvents API、NSWorkspaceとの連携
データフロー Combine + Async/Await スキャン進捗のリアクティブな更新とバックグラウンド処理

プライバシーとデータ保護

すべてのスキャンおよび削除操作はローカル環境で完結します。テレメトリデータやファイルリストが外部サーバーに送信されることはなく、エアギャップ環境でも完全に機能します。

ベストプラクティスとデータ保護

  • 選択的クリーンアップ: クリエイティブソフトウェアのプリセットやデータベース接続設定など、再利用性の高い設定ファイルはスキャン結果から除外することを推奨します。
  • Time Machineとの連携: 大規模なクリーンアップを実行する前は、必ずTime Machineまたは別のバックアップソリューションでシステムのスナップショットを取得してください。
  • ドライランモードの活用: CLIの--dry-runフラグを使用すると、実際の削除を行わずに検出されるファイルのリストと合計サイズを確認できます。

CLIリファレンスと高度なオプション

GUIだけでなく、CI/CDパイプラインやcronジョブに組み込むための包括的なCLIインターフェースを提供します。


# シンボリックリンクの作成(パスを通す)
ln -s /Applications/Pearcleaner.app/Contents/MacOS/Pearcleaner /usr/local/bin/pear

# 特定バンドルIDの残留ファイルをスキャン(削除なし)
pear scan com.example.app --dry-run

# 孤立したキャッシュファイルのみのクリーンアップ
pear cleanup --orphaned --target caches

# アプリケーションのアーキテクチャ最適化(Rosetta 2用のx86_64バイナリを削除)
pear optimize --strip-arch x86_64 --app /Applications/TargetApp.app

よくある質問(FAQ)

Pearcleanerがシステムファイルを誤削除するリスクはありますか?

リスクは最小限に抑えられています。内部にハードコードされたホワイトリストと、macOSのSIP(System Integrity Protection)によって保護されているパスへの書き込み/削除は、OSレベルでブロックされます。

削除したファイルを復元できますか?

デフォルトでは、ファイルは直接削除されず、macOSのゴミ箱(~/.Trash)に移動されます。これにより、誤って重要な設定ファイルを削除した場合でも、ゴミ箱から復元可能です。--forceフラグを使用した場合のみ、完全な削除(バイパス)が行われます。

サポートされているmacOSのバージョンは?

macOSバージョン サポート状況 特記事項
macOS 13.x (Ventura) 完全サポート 基本機能すべて利用可能
macOS 14.x (Sonoma) 完全サポート Game Mode関連のAPI最適化済み
macOS 15.x (Sequoia) 完全サポート 最新のプライバシーAPI(TCC)に対応
Pearcleaner Icon

タグ: macOS Swift SwiftUI Homebrew FSEvents

9月29日 14:03 投稿