diff --git a/Gemfile.lock b/Gemfile.lock index 48a2d59..637f3a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,16 +10,16 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.943.0) + aws-partitions (1.944.0) aws-sdk-core (3.197.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.83.0) + aws-sdk-kms (1.85.0) aws-sdk-core (~> 3, >= 3.197.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.152.1) + aws-sdk-s3 (1.152.3) aws-sdk-core (~> 3, >= 3.197.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) @@ -68,7 +68,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.220.0) + fastlane (2.221.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -152,9 +152,9 @@ GEM httpclient (2.8.3) jmespath (1.6.2) json (2.7.2) - jwt (2.8.1) + jwt (2.8.2) base64 - mini_magick (4.12.0) + mini_magick (4.13.1) mini_mime (1.1.5) multi_json (1.15.0) multipart-post (2.4.1) @@ -164,7 +164,7 @@ GEM optparse (0.5.0) os (1.1.4) plist (3.7.1) - public_suffix (5.0.5) + public_suffix (5.1.1) rake (13.2.1) representable (3.2.0) declarative (< 0.1.0) diff --git a/Pearcleaner.xcodeproj/project.pbxproj b/Pearcleaner.xcodeproj/project.pbxproj index f49a3ae..a43a094 100644 --- a/Pearcleaner.xcodeproj/project.pbxproj +++ b/Pearcleaner.xcodeproj/project.pbxproj @@ -33,6 +33,7 @@ C77B90232AF2D616009CC655 /* FilesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90222AF2D616009CC655 /* FilesView.swift */; }; C78121662BC892A000BE06BD /* FinderOpen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C78121652BC892A000BE06BD /* FinderOpen.swift */; }; C781216B2BC892A000BE06BD /* FinderOpen.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = C78121632BC892A000BE06BD /* FinderOpen.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + C786F1A82C29E599001B3800 /* Builds in Resources */ = {isa = PBXBuildFile; fileRef = C786F1A72C29E599001B3800 /* Builds */; }; C79D05202BC99EB40083F976 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79D051F2BC99EB40083F976 /* ThemeManager.swift */; }; C7A27E812AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist in Resources */ = {isa = PBXBuildFile; fileRef = C7A27E802AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist */; }; C7A2AE5D2B9644F300161540 /* FileWatcher in Frameworks */ = {isa = PBXBuildFile; productRef = C7A2AE5C2B9644F300161540 /* FileWatcher */; }; @@ -131,6 +132,7 @@ C78121652BC892A000BE06BD /* FinderOpen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinderOpen.swift; sourceTree = ""; }; C78121672BC892A000BE06BD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C78121682BC892A000BE06BD /* FinderOpen.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FinderOpen.entitlements; sourceTree = ""; }; + C786F1A72C29E599001B3800 /* Builds */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Builds; sourceTree = SOURCE_ROOT; }; C79D051F2BC99EB40083F976 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = ""; }; C7A27E7F2AFD70DD00166168 /* PearcleanerSentinel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PearcleanerSentinel.entitlements; sourceTree = ""; }; C7A27E802AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.alienator88.PearcleanerSentinel.plist; sourceTree = ""; }; @@ -291,6 +293,7 @@ isa = PBXGroup; children = ( C77B90032AF18E2E009CC655 /* PearcleanerApp.swift */, + C786F1A72C29E599001B3800 /* Builds */, C76D084F2AF84E0100D07867 /* Resources */, C76D08462AF83C2F00D07867 /* Settings */, C77B901F2AF1B390009CC655 /* Views */, @@ -438,6 +441,7 @@ buildActionMask = 2147483647; files = ( C77B90082AF18E2F009CC655 /* Assets.xcassets in Resources */, + C786F1A82C29E599001B3800 /* Builds in Resources */, C7A27E812AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist in Resources */, C7DC1FEE2B9F7D3D009AC317 /* features.json in Resources */, ); @@ -572,8 +576,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 49; - APP_VERSION = 3.7.4; + APP_BUILD = 50; + APP_VERSION = 3.7.5; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -642,8 +646,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 49; - APP_VERSION = 3.7.4; + APP_BUILD = 50; + APP_VERSION = 3.7.5; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/Pearcleaner.xcodeproj/xcshareddata/xcschemes/Pearcleaner Debug.xcscheme b/Pearcleaner.xcodeproj/xcshareddata/xcschemes/Pearcleaner Debug.xcscheme index a14dc88..a023c31 100644 --- a/Pearcleaner.xcodeproj/xcshareddata/xcschemes/Pearcleaner Debug.xcscheme +++ b/Pearcleaner.xcodeproj/xcshareddata/xcschemes/Pearcleaner Debug.xcscheme @@ -49,6 +49,13 @@ ReferencedContainer = "container:Pearcleaner.xcodeproj"> + + + + + + + + + + + + + + diff --git a/Pearcleaner/Logic/AppInfoFetch.swift b/Pearcleaner/Logic/AppInfoFetch.swift index 96d605d..32a561c 100644 --- a/Pearcleaner/Logic/AppInfoFetch.swift +++ b/Pearcleaner/Logic/AppInfoFetch.swift @@ -58,7 +58,7 @@ class AppInfoFetcher { let system = !path.path.contains(NSHomeDirectory()) return AppInfo(id: UUID(), path: path, bundleIdentifier: bundleIdentifier, appName: appName, appVersion: appVersion, appIcon: appIcon, - webApp: webApp, wrapped: wrapped, system: system, bundleSize: 0, files: [], fileSize: [:], fileSizeLogical: [:], fileIcon: [:]) + webApp: webApp, wrapped: wrapped, system: system, arch: .empty, bundleSize: 0, files: [], fileSize: [:], fileSizeLogical: [:], fileIcon: [:]) } private static func fetchAppIcon(for path: URL, wrapped: Bool) -> NSImage? { diff --git a/Pearcleaner/Logic/AppPathsFetch.swift b/Pearcleaner/Logic/AppPathsFetch.swift index 9895626..05f6330 100644 --- a/Pearcleaner/Logic/AppPathsFetch.swift +++ b/Pearcleaner/Logic/AppPathsFetch.swift @@ -33,10 +33,16 @@ class AppPathFinder { func findPaths() { Task(priority: .background) { - self.initialURLProcessing() - self.collectDirectories() - self.collectFiles() - self.finalizeCollection() + if self.appInfo.webApp { + self.initialURLProcessing() + self.finalizeCollection() + } else { + self.initialURLProcessing() + self.collectDirectories() + self.collectFiles() + self.finalizeCollection() + } + } } diff --git a/Pearcleaner/Logic/AppState.swift b/Pearcleaner/Logic/AppState.swift index 04fd5ea..1841e15 100644 --- a/Pearcleaner/Logic/AppState.swift +++ b/Pearcleaner/Logic/AppState.swift @@ -48,6 +48,7 @@ class AppState: ObservableObject { webApp: false, wrapped: false, system: false, + arch: .empty, bundleSize: 0, files: [], fileSize: [:], @@ -100,6 +101,7 @@ struct AppInfo: Identifiable, Equatable, Hashable { let webApp: Bool let wrapped: Bool let system: Bool + var arch: Arch var bundleSize: Int64 var files: [URL] var fileSize: [URL:Int64] @@ -115,7 +117,7 @@ struct AppInfo: Identifiable, Equatable, Hashable { } - static let empty = AppInfo(id: UUID(), path: URL(fileURLWithPath: ""), bundleIdentifier: "", appName: "", appVersion: "", appIcon: nil, webApp: false, wrapped: false, system: false, bundleSize: 0, files: [], fileSize: [:], fileSizeLogical: [:], fileIcon: [:]) + static let empty = AppInfo(id: UUID(), path: URL(fileURLWithPath: ""), bundleIdentifier: "", appName: "", appVersion: "", appIcon: nil, webApp: false, wrapped: false, system: false, arch: .empty, bundleSize: 0, files: [], fileSize: [:], fileSizeLogical: [:], fileIcon: [:]) } @@ -141,7 +143,12 @@ struct ZombieFile: Identifiable, Equatable, Hashable { } - +enum Arch { + case arm + case intel + case universal + case empty +} enum CurrentTabView:Int diff --git a/Pearcleaner/Logic/Utilities.swift b/Pearcleaner/Logic/Utilities.swift index 41f4ede..84ac9ab 100644 --- a/Pearcleaner/Logic/Utilities.swift +++ b/Pearcleaner/Logic/Utilities.swift @@ -252,6 +252,50 @@ func isRestricted(atPath path: URL) -> Bool { } +// Check app bundle architecture +func checkAppBundleArchitecture(at appBundlePath: String) -> Arch { + + let bundleURL = URL(fileURLWithPath: appBundlePath) + let executableName: String + + // Extract the executable name from Info.plist + let infoPlistPath = bundleURL.appendingPathComponent("Contents/Info.plist") + if let infoPlist = NSDictionary(contentsOf: infoPlistPath) as? [String: Any], + let bundleExecutable = infoPlist["CFBundleExecutable"] as? String { + executableName = bundleExecutable + } else { + return .empty + } + + let executablePath = bundleURL.appendingPathComponent("Contents/MacOS/\(executableName)").path + + let task = Process() + task.launchPath = "/usr/bin/lipo" + task.arguments = ["-archs", executablePath] + + let pipe = Pipe() + task.standardOutput = pipe + task.launch() + + let data = pipe.fileHandleForReading.readDataToEndOfFile() + task.waitUntilExit() + + if let output = String(data: data, encoding: .utf8) { + let architectures = output.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: " ") + if architectures.contains("x86_64") && architectures.contains("arm64") { + return .universal + } else if architectures.contains("x86_64") { + return .intel + } else if architectures.contains("arm64") { + return .arm + } + } + + return .empty +} + + + // Convert icon to png so colors render correctly func convertICNSToPNG(icon: NSImage, size: NSSize) -> NSImage? { // Resize the icon to the specified size diff --git a/Pearcleaner/PearcleanerApp.swift b/Pearcleaner/PearcleanerApp.swift index 0e67060..0020f0a 100644 --- a/Pearcleaner/PearcleanerApp.swift +++ b/Pearcleaner/PearcleanerApp.swift @@ -31,7 +31,6 @@ struct PearcleanerApp: App { var body: some Scene { - WindowGroup { Group { if !mini { diff --git a/Pearcleaner/Views/AppListItems.swift b/Pearcleaner/Views/AppListItems.swift index 1f9746d..4e3af14 100644 --- a/Pearcleaner/Views/AppListItems.swift +++ b/Pearcleaner/Views/AppListItems.swift @@ -137,6 +137,14 @@ struct AppListItems: View { appState.sortedApps[index].bundleSize = size } } + + let architecture = checkAppBundleArchitecture(at: appInfo.path.path) + updateOnMain { + // Optionally, update the appInfo in the appState array + if let index = appState.sortedApps.firstIndex(where: { $0.path == appInfo.path }) { + appState.sortedApps[index].arch = architecture + } + } } } diff --git a/Pearcleaner/Views/FilesView.swift b/Pearcleaner/Views/FilesView.swift index 49204f3..f805d5c 100644 --- a/Pearcleaner/Views/FilesView.swift +++ b/Pearcleaner/Views/FilesView.swift @@ -180,8 +180,10 @@ struct FilesView: View { .foregroundStyle(Color("mode").opacity(0.5)) .frame(minWidth: 30, minHeight: 15) .padding(2) + .padding(.horizontal, 2) .background(Color("mode").opacity(0.1)) .clipShape(.capsule) + .help("This is a web app") } @@ -191,8 +193,23 @@ struct FilesView: View { .foregroundStyle(Color("mode").opacity(0.5)) .frame(minWidth: 30, minHeight: 15) .padding(2) + .padding(.horizontal, 2) .background(Color("mode").opacity(0.1)) .clipShape(.capsule) + .help("This is a wrapped iOS app") + + } + + if appState.appInfo.arch != .empty { + Text("\(appState.appInfo.arch)") + .font(.footnote) + .foregroundStyle(Color("mode").opacity(0.5)) + .frame(minWidth: 30, minHeight: 15) + .padding(2) + .padding(.horizontal, 2) + .background(Color("mode").opacity(0.1)) + .clipShape(.capsule) + .help("This bundle's architecture is \(appState.appInfo.arch)") } @@ -204,6 +221,7 @@ struct FilesView: View { .padding(.horizontal, 2) .background(Color("mode").opacity(0.1)) .clipShape(.capsule) + .help("This app is located in \(appState.appInfo.system ? "/Applications" : "\(home)")") }