Move to AF swift package

This commit is contained in:
Alin
2024-07-16 15:09:31 -06:00
parent cf5143f17d
commit b415f74557
29 changed files with 33 additions and 1792 deletions
-58
View File
@@ -18,18 +18,12 @@ class AppState: ObservableObject {
@Published var zombieFile: ZombieFile
@Published var sortedApps: [AppInfo] = []
@Published var selectedItems = Set<URL>()
@Published var alertType = AlertType.off
@Published var currentView = CurrentDetailsView.empty
@Published var showAlert: Bool = false
@Published var sidebar: Bool = true
@Published var progressBar: (String, Double) = ("Ready", 0.0)
@Published var reload: Bool = false
@Published var showProgress: Bool = false
@Published var finderExtensionEnabled: Bool = false
// @Published var updateAvailable: Bool = false
@Published var featureAvailable: Bool = false
// @Published var permissionsOkay: Bool = true
// @Published var permissionResults: PermissionsCheckResults?
@Published var showUninstallAlert: Bool = false
@Published var oneShotMode: Bool = false
@Published var showConditionBuilder: Bool = false
@@ -178,55 +172,3 @@ enum CurrentDetailsView:Int
case apps
case zombie
}
enum NewWindow:Int
{
case perm
case feature
}
enum AlertType:Int
{
case diskAccess
case update
case no_update
case restartApp
case off
}
enum DisplayMode: Int, CaseIterable {
case system, dark, light
var colorScheme: ColorScheme? {
get {
switch self {
case .system: return nil
case .dark: return ColorScheme.dark
case .light: return ColorScheme.light
}
}
set {
switch newValue {
case .none:
self = .system
case .dark?:
self = .dark
case .light?:
self = .light
default:
break
}
}
}
var description: String {
switch self {
case .system: return "System"
case .dark: return "Dark"
case .light: return "Light"
}
}
}
-113
View File
@@ -1,113 +0,0 @@
//
// Authorization.swift
// Pearcleaner
//
// Created by Alin Lupascu on 2/23/24.
//
//import Foundation
//import Swift
//
//// https://github.com/x13a/authorization-swift
//
//public struct Authorization {
//
// public enum Error: Swift.Error {
// case create(OSStatus)
// case copyRights(OSStatus)
// case exec(OSStatus)
// }
//
// public static func executeWithPrivileges(
// _ command: String
// ) -> Result<FileHandle, Error> {
//
// let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
// var fn: @convention(c) (
// AuthorizationRef,
// UnsafePointer<CChar>, // path
// AuthorizationFlags,
// UnsafePointer<UnsafePointer<CChar>?>, // args
// UnsafeMutablePointer<UnsafeMutablePointer<FILE>>?
// ) -> OSStatus
// fn = unsafeBitCast(
// dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges"),
// to: type(of: fn)
// )
//
// var authorizationRef: AuthorizationRef? = nil
// var err = AuthorizationCreate(nil, nil, [], &authorizationRef)
// guard err == errAuthorizationSuccess else {
// return .failure(.create(err))
// }
// defer { AuthorizationFree(authorizationRef!, [.destroyRights]) }
//
// var components = command.components(separatedBy: " ")
// var path = components.remove(at: 0).cString(using: .utf8)!
// let name = kAuthorizationRightExecute.cString(using: .utf8)!
//
// var items: AuthorizationItem = name.withUnsafeBufferPointer { nameBuf in
// path.withUnsafeBufferPointer { pathBuf in
// let pathPtr =
// UnsafeMutableRawPointer(mutating: pathBuf.baseAddress!)
// return AuthorizationItem(
// name: nameBuf.baseAddress!,
// valueLength: path.count,
// value: pathPtr,
// flags: 0
// )
// }
// }
//
// var rights: AuthorizationRights =
// withUnsafeMutablePointer(to: &items) { items in
// return AuthorizationRights(count: 1, items: items)
// }
//
// let flags: AuthorizationFlags = [
// .interactionAllowed,
// .preAuthorize,
// .extendRights,
// ]
//
// err = AuthorizationCopyRights(
// authorizationRef!,
// &rights,
// nil,
// flags,
// nil
// )
// guard err == errAuthorizationSuccess else {
// return .failure(.copyRights(err))
// }
//
// let rest = components.map { $0.cString(using: .utf8)! }
// var args = Array<UnsafePointer<CChar>?>(
// repeating: nil,
// count: rest.count + 1
// )
// for (idx, arg) in rest.enumerated() {
// args[idx] = UnsafePointer<CChar>?(arg)
// }
//
// var file = FILE()
// let fh: FileHandle?
//
// (err, fh) = withUnsafeMutablePointer(to: &file) { file in
// var pipe = file
// let err = fn(authorizationRef!, &path, [], &args, &pipe)
// guard err == errAuthorizationSuccess else {
// return (err, nil)
// }
// let fh = FileHandle(
// fileDescriptor: fileno(pipe),
// closeOnDealloc: true
// )
// return (err, fh)
// }
// guard err == errAuthorizationSuccess else {
// return .failure(.exec(err))
// }
// return .success(fh!)
// }
//}
-113
View File
@@ -1,113 +0,0 @@
//
// Features.swift
// Pearcleaner
//
// Created by Alin Lupascu on 3/1/24.
//
import Foundation
import SwiftUI
import AlinFoundation
func getFeatures(appState: AppState, features: Binding<String>) {
let url = URL(string: "https://api.github.com/repos/alienator88/Pearcleaner/contents/features.json")!
var request = URLRequest(url: url)
request.setValue("application/vnd.github.VERSION.raw", forHTTPHeaderField: "Accept")
request.setValue("2022-11-28", forHTTPHeaderField: "X-GitHub-Api-Version")
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
if let jsonDict = jsonObject as? [String: String],
let bundleVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
let featureText = jsonDict[bundleVersion] {
if features.wrappedValue != featureText.featureFormat() {
updateOnMain {
features.wrappedValue = featureText.featureFormat()
appState.featureAvailable = true
// show.wrappedValue = true
}
} else {
updateOnMain {
appState.featureAvailable = false
}
// show.wrappedValue = false
// print("Same version")
}
} else {
updateOnMain {
appState.featureAvailable = false
}
// show.wrappedValue = false
// print("No features for version found")
}
} catch {
printOS("Error reading features JSON from GitHub: \(error.localizedDescription)")
}
} else {
printOS("Error reading features JSON from GitHub: \(error?.localizedDescription ?? "Unknown error")")
}
}.resume()
}
struct FeatureNotificationView: View {
let appState: AppState
@State private var hovered: Bool = false
var body: some View {
HStack {
Text("New Features!")
.font(.callout)
.opacity(0.5)
.padding(.leading, 7)
Spacer()
HStack(alignment: .center) {
Image(systemName: !hovered ? "star" : "star.fill")
.resizable()
.scaledToFit()
.frame(width: 14, height: 14)
.animation(.easeInOut(duration: 0.2), value: hovered)
.foregroundStyle(.white)
Text("Check")
.foregroundStyle(.white)
}
.padding(3)
.onHover { hovering in
withAnimation() {
hovered = hovering
}
}
.onTapGesture {
NewWin.show(appState: appState, width: 500, height: 400, newWin: .feature)
updateOnMain {
appState.featureAvailable = false
}
}
.help("View latest features")
.padding(.horizontal, 5)
.padding(.vertical, 4)
.background(Color("pear"))
.clipShape(RoundedRectangle(cornerRadius: 6))
}
.frame(height: 30)
.padding(5)
.background(.primary.opacity(0.05))
.clipShape(RoundedRectangle(cornerRadius: 6))
.padding(.horizontal)
.padding(.bottom)
}
}
-53
View File
@@ -146,59 +146,6 @@ struct RescanButton: ButtonStyle {
public struct NewFeatureView: View {
var text: String
var mini: Bool
@Binding var showFeature: Bool
public var body: some View {
VStack {
HStack {
Image(systemName: "star")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
Text("New features for v\((Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String)!)!")
.font(.headline).bold()
Spacer()
Button("") {
withAnimation(Animation.easeInOut(duration: 0.5)) {
showFeature = false
}
}
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close", color: (.primary.opacity(0.5))))
.onHover { isHovered in
if isHovered {
NSCursor.pointingHand.push()
} else {
NSCursor.pop()
}
}
}
.padding()
ScrollView() {
Text(text)
.font(.callout)
.multilineTextAlignment(.leading)
.padding(.trailing)
}
.padding(.horizontal)
.padding(.bottom)
}
.frame(maxWidth: mini ? 200 : 400, maxHeight: mini ? 300 : 250)
.background(.ultraThinMaterial)
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.strokeBorder(.primary.opacity(0.2), lineWidth: 0.6)
)
.padding()
}
}
public struct SlideableDivider: View {
@EnvironmentObject private var themeManager: ThemeManager
@Binding var dimension: Double
-100
View File
@@ -1,100 +0,0 @@
//
// Trash.swift
// Pearcleaner
//
// Created by Alin Lupascu on 3/18/24.
//
import Foundation
//class TrashManager {
// private var fileLocations: [URL: URL] = [:] // Maps original location to trash location
//
// // Function to move files to Trash and track their original locations
// func delete(fileURLs: [URL]) {
// let fileManager = FileManager.default
// let trashURL = try! fileManager.url(for: .trashDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
//
// for originalURL in fileURLs {
// let destinationURL = trashURL.appendingPathComponent(originalURL.lastPathComponent)
//
// do {
// // The resultingItemURL is the new location of the item in the trash.
// var resultingItemURL: NSURL?
// try fileManager.trashItem(at: originalURL, resultingItemURL: &resultingItemURL)
// if let trashPath = resultingItemURL as URL? {
// // Track the original URL and its corresponding location in the Trash
// fileLocations[originalURL] = trashPath
// }
// } catch {
// print("Error moving file to Trash: \(error)")
// }
// }
// }
//
// // Function to undo the deletion of files, moving them back from Trash to their original locations
// func undoDelete() {
// let fileManager = FileManager.default
//
// for (originalURL, trashURL) in fileLocations {
// do {
// // Attempt to move the item back to its original location
// try fileManager.moveItem(at: trashURL, to: originalURL)
// } catch {
// print("Error moving file back from Trash: \(error)")
// }
// }
//
// // Clear the tracking dictionary after undoing the deletions
// fileLocations.removeAll()
// }
//}
//import Foundation
//
//func moveToTrash(fileURLs: [URL], completion: @escaping () -> Void = {}) {
// for fileURL in fileURLs {
// do {
// try _ = fileURL.checkResourceIsReachable()
// } catch {
// printOS(error.localizedDescription)
// }
// }
// let target: NSAppleEventDescriptor = .init(bundleIdentifier: "com.apple.finder")
// let event: NSAppleEventDescriptor = .init(eventClass: kAECoreSuite,
// eventID: AEEventID(kAEDelete),
// targetDescriptor: target,
// returnID: AEReturnID(kAutoGenerateReturnID),
// transactionID: AETransactionID(kAnyTransactionID))
// let fileList: NSAppleEventDescriptor = fileURLs.enumerated().reduce(into: .init(listDescriptor: ())) {
// (result: inout NSAppleEventDescriptor, element: (offset: Int, element: URL)) in
// if let nativePath: NSAppleEventDescriptor = .init(
// descriptorType: typeFileURL,
// data: element.element.absoluteString.data(using: .utf8)
// ) {
// result.insert(nativePath, at: element.offset + 1)
// }
// }
//
// event.setParam(fileList, forKeyword: keyDirectObject)
//
// do {
// try event.sendEvent(options: .noReply, timeout: TimeInterval(kAEDefaultTimeout))
// } catch let error as NSError {
// if case -600 = error.code {
// printOS("Finder is not running.")
// } else {
// printOS(error.description)
// }
// }
//
// completion()
//}
+1 -386
View File
@@ -10,27 +10,6 @@ import SwiftUI
import AlinFoundation
import AppKit
// Make updates on main thread
//func updateOnMain(after delay: Double? = nil, _ updates: @escaping () -> Void) {
// if let delay = delay {
// DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
// updates()
// }
// } else {
// DispatchQueue.main.async {
// updates()
// }
// }
//}
//
//
//// Execute functions on background thread
//func updateOnBackground(_ updates: @escaping () -> Void) {
// DispatchQueue.global(qos: .userInitiated).async {
// updates()
// }
//}
// Reload apps list
func reloadAppsList(appState: AppState, fsm: FolderSettingsManager) {
appState.reload = true
@@ -54,60 +33,6 @@ func resizeWindowAuto(windowSettings: WindowSettings, title: String) {
}
// Check app directory based on user permission
//func checkAppDirectoryAndUserRole(completion: @escaping ((isInCorrectDirectory: Bool, isAdmin: Bool)) -> Void) {
// isCurrentUserAdmin { isAdmin in
// let bundlePath = Bundle.main.bundlePath as NSString
// let applicationsDir = "/Applications"
// let userApplicationsDir = "\(home)/Applications"
//
// var isInCorrectDirectory = false
//
// if isAdmin {
// // Admins can have the app in either /Applications or ~/Applications
// isInCorrectDirectory = bundlePath.deletingLastPathComponent == applicationsDir ||
// bundlePath.deletingLastPathComponent == userApplicationsDir
// } else {
// // Standard users should only have the app in ~/Applications
// isInCorrectDirectory = bundlePath.deletingLastPathComponent == userApplicationsDir
// }
//
// // Return both conditions: if the app is in the correct directory and if the user is an admin
// completion((isInCorrectDirectory, isAdmin))
// }
//}
//
//
//// Check if user is admin or standard user
//func isCurrentUserAdmin(completion: @escaping (Bool) -> Void) {
// let process = Process()
// process.executableURL = URL(fileURLWithPath: "/bin/zsh") // Using zsh, macOS default shell
// process.arguments = ["-c", "groups $(whoami) | grep -q ' admin '"]
//
// process.terminationHandler = { process in
// // On macOS, a process's exit status of 0 indicates success (admin group found in this context)
// completion(process.terminationStatus == 0)
// }
//
// do {
// try process.run()
// } catch {
// print("Failed to execute command: \(error)")
// completion(false)
// }
//}
// Check if appearance is dark mode
//func isDarkMode() -> Bool {
// return NSApp.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
//}
//
//// Set app color mode
//func setAppearance(mode: NSAppearance.Name) {
// NSApp.appearance = NSAppearance(named: mode)
//}
// Check if Pearcleaner has any windows open
func hasWindowOpen() -> Bool {
for window in NSApp.windows where window.title == "Pearcleaner" {
@@ -116,14 +41,7 @@ func hasWindowOpen() -> Bool {
return false
}
// Find and hide/show main app window when using menubar item
//func findAndHideWindows(named titles: [String]) {
// for title in titles {
// if let window = NSApp.windows.first(where: { $0.title == title }) {
// window.close()
// }
// }
//}
func findAndSetWindowFrame(named titles: [String], windowSettings: WindowSettings) {
windowSettings.registerDefaultWindowSettings() {
@@ -138,22 +56,6 @@ func findAndSetWindowFrame(named titles: [String], windowSettings: WindowSetting
}
//func findAndShowWindows(named titles: [String]) {
// for title in titles {
// if let window = NSApp.windows.first(where: { $0.title == title }) {
// window.makeKeyAndOrderFront(nil)
// }
// }
//}
// Copy to clipboard
//func copyToClipboard(text: String) {
// let pasteboard = NSPasteboard.general
// pasteboard.clearContents()
// pasteboard.setString(text, forType: .string)
//}
// Brew cleanup
@@ -222,17 +124,6 @@ func saveURLsToFile(urls: Set<URL>, appState: AppState) {
}
// Check if symlink
//func isSymlink(atPath path: URL) -> Bool {
// do {
// let _ = try path.checkResourceIsReachable()
// let resourceValues = try path.resourceValues(forKeys: [.isSymbolicLinkKey])
// return resourceValues.isSymbolicLink == true
// } catch {
// return false
// }
//}
// Open trash folder
func openTrash() {
@@ -295,64 +186,6 @@ func checkAppBundleArchitecture(at appBundlePath: String) -> Arch {
// Convert icon to png so colors render correctly
//func convertICNSToPNG(icon: NSImage, size: NSSize) -> NSImage? {
// // Resize the icon to the specified size
// let resizedIcon = NSImage(size: size)
// resizedIcon.lockFocus()
// icon.draw(in: NSRect(x: 0, y: 0, width: size.width, height: size.height))
// resizedIcon.unlockFocus()
//
// // Convert the resized icon to PNG format
// if let resizedImageData = resizedIcon.tiffRepresentation,
// let resizedBitmap = NSBitmapImageRep(data: resizedImageData),
// let pngData = resizedBitmap.representation(using: .png, properties: [:]) {
// return NSImage(data: pngData)
// }
//
// return nil
//}
// Get icon for files and folders
//func getIconForFileOrFolder(atPath path: URL) -> Image? {
// return Image(nsImage: NSWorkspace.shared.icon(forFile: path.path))
//}
//
//func getIconForFileOrFolderNS(atPath path: URL) -> NSImage? {
// return NSWorkspace.shared.icon(forFile: path.path)
//}
// Get average color from image
//extension NSImage {
// var averageColor: NSColor? {
// guard let tiffData = self.tiffRepresentation, let bitmapImage = NSBitmapImageRep(data: tiffData), let inputImage = CIImage(bitmapImageRep: bitmapImage) else { return nil }
//
// let extentVector = CIVector(x: inputImage.extent.origin.x, y: inputImage.extent.origin.y, z: inputImage.extent.size.width, w: inputImage.extent.size.height)
//
// guard let filter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: inputImage, kCIInputExtentKey: extentVector]) else { return nil }
// guard let outputImage = filter.outputImage else { return nil }
//
// var bitmap = [UInt8](repeating: 0, count: 4)
// let context = CIContext(options: [.workingColorSpace: NSNull()])
// context.render(outputImage, toBitmap: &bitmap, rowBytes: 4, bounds: CGRect(x: 0, y: 0, width: 1, height: 1), format: CIFormat.RGBA8, colorSpace: nil)
//
// return NSColor(red: CGFloat(bitmap[0]) / 255, green: CGFloat(bitmap[1]) / 255, blue: CGFloat(bitmap[2]) / 255, alpha: CGFloat(bitmap[3]) / 255)
// }
//}
// Relaunch app
//func relaunchApp(afterDelay seconds: TimeInterval = 0.5) -> Never {
// let task = Process()
// task.launchPath = "/bin/sh"
// task.arguments = ["-c", "sleep \(seconds); open \"\(Bundle.main.bundlePath)\""]
// task.launch()
//
// NSApp.terminate(nil)
// exit(0)
//}
// Check if app is running before deleting app files
func killApp(appId: String, completion: @escaping () -> Void = {}) {
let runningApps = NSWorkspace.shared.runningApplications
@@ -459,23 +292,6 @@ func isNested(path: URL) -> Bool {
return parentDirectory != applicationsPath && parentDirectory != homeApplicationsPath
}
// Check if macOS is 14.0 or higher
//func isMacOS14OrHigher() -> Bool {
// if #available(macOS 14, *) {
// return true
// } else {
// return false
// }
//}
// --- Extend Int to convert hours to seconds ---
//extension Int {
// var daysToSeconds: Double {
// return Double(self) * 24 * 60 * 60
// }
//}
// --- Extend String to remove periods, spaces and lowercase the string
@@ -486,23 +302,6 @@ extension String {
}
}
// --- Extend string to replace - and | with custom characters
extension String {
func featureFormat() -> String {
return self.replacingOccurrences(of: "- ", with: "").replacingOccurrences(of: "|", with: "\n\n")
}
}
// --- Capitalize first letter of string only
//extension String {
// func capitalizingFirstLetter() -> String {
// return prefix(1).capitalized + dropFirst()
// }
//
// mutating func capitalizeFirstLetter() {
// self = self.capitalizingFirstLetter()
// }
//}
// --- Returns comma separated string as array of strings
extension String {
@@ -515,130 +314,6 @@ extension String {
}
// --- Overload the greater than operator ">" to do a semantic check on the string versions
//extension String {
// func versionStringToTuple() -> (Int, Int, Int) {
// let components = self.split(separator: ".").compactMap { Int($0) }
// return (components[0], components[1], components[2])
// }
//
// static func > (lhs: String, rhs: String) -> Bool {
// let lhsVersion = lhs.versionStringToTuple()
// let rhsVersion = rhs.versionStringToTuple()
// return lhsVersion > rhsVersion
// }
//}
// --- Trash Relationship ---
//extension FileManager {
// public func isInTrash(_ file: URL) -> Bool {
// var relationship: URLRelationship = .other
// try? getRelationship(&relationship, of: .trashDirectory, in: .userDomainMask, toItemAt: file)
// return relationship == .contains
// }
//}
// --- Extend print command to also output to the Console ---
//func printOS(_ items: Any..., separator: String = " ", terminator: String = "\n") {
// let message = items.map { "\($0)" }.joined(separator: separator)
// let log = OSLog(subsystem: "pearcleaner", category: "Application")
// os_log("%@", log: log, type: .default, message)
//}
// Get size of files
//func totalSizeOnDisk(for paths: [URL]) -> (real: Int64, logical: Int64) {
// let fileManager = FileManager.default
// var totalAllocatedSize: Int64 = 0
// var totalFileSize: Int64 = 0
//
// for url in paths {
// var isDirectory: ObjCBool = false
// if fileManager.fileExists(atPath: url.path, isDirectory: &isDirectory) {
// let keys: [URLResourceKey] = [.totalFileAllocatedSizeKey, .fileSizeKey]
// if isDirectory.boolValue {
// // It's a directory, recurse into it
// if let enumerator = fileManager.enumerator(at: url, includingPropertiesForKeys: keys, errorHandler: nil) {
// for case let fileURL as URL in enumerator {
// do {
// let fileAttributes = try fileURL.resourceValues(forKeys: Set(keys))
// if let allocatedSize = fileAttributes.totalFileAllocatedSize {
// totalAllocatedSize += Int64(allocatedSize)
// }
// if let fileSize = fileAttributes.fileSize {
// totalFileSize += Int64(fileSize)
// }
// } catch {
// print("Error getting file attributes for \(fileURL): \(error)")
// }
// }
// }
// } else {
// // It's a file
// do {
// let fileAttributes = try url.resourceValues(forKeys: Set(keys))
// if let allocatedSize = fileAttributes.totalFileAllocatedSize {
// totalAllocatedSize += Int64(allocatedSize)
// }
// if let fileSize = fileAttributes.fileSize {
// totalFileSize += Int64(fileSize)
// }
// } catch {
// print("Error getting file attributes for \(url): \(error)")
// }
// }
// }
// }
//
// return (real: totalAllocatedSize, logical: totalFileSize)
//}
//
//
//
//func totalSizeOnDisk(for path: URL) -> (real: Int64, logical: Int64) {
// return totalSizeOnDisk(for: [path])
//}
//
//// ByteFormatter
//func formatByte(size: Int64) -> (human: String, byte: String) {
// let byteCountFormatter = ByteCountFormatter()
// byteCountFormatter.countStyle = .file
// byteCountFormatter.allowedUnits = [.useAll]
// let human = byteCountFormatter.string(fromByteCount: size)
//
// let numberformatter = NumberFormatter()
// numberformatter.numberStyle = .decimal
// let formattedNumber = numberformatter.string(from: NSNumber(value: size)) ?? "\(size)"
// let byte = "\(formattedNumber)"
//
// return (human: human, byte: byte)
//
//}
// Only process supported files
//func isSupportedFileType(at path: String) -> Bool {
// let fileManager = FileManager.default
// do {
// let attributes = try fileManager.attributesOfItem(atPath: path)
// if let fileType = attributes[FileAttributeKey.type] as? FileAttributeType {
// switch fileType {
// case .typeRegular, .typeDirectory, .typeSymbolicLink:
// // The file is a regular file, directory, or symbolic link
// return true
// default:
// // The file is a socket, pipe, or another type not supported
// return false
// }
// }
// } catch {
// printOS("Error getting file attributes: \(error)")
// }
// return false
//}
// --- Pearcleaner Uninstall --
@@ -668,47 +343,6 @@ func uninstallPearcleaner(appState: AppState, locations: Locations) {
}
// --- Create Application Support folder if it doesn't exist ---
//func ensureApplicationSupportFolderExists(appState: AppState) {
// let fileManager = FileManager.default
// let supportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("com.alienator88.Pearcleaner")
//
// // Check to make sure Application Support/Pearcleaner folder exists
// if !fileManager.fileExists(atPath: supportURL.path) {
// try! fileManager.createDirectory(at: supportURL, withIntermediateDirectories: true)
// printOS("Created Application Support/com.alienator88.Pearcleaner folder")
// }
//}
// --- Write Log to File for troubleshooting ---
//func writeLog(string: String) {
// let fileManager = FileManager.default
// let home = fileManager.homeDirectoryForCurrentUser.path
// let logFilePath = "\(home)/Downloads/log.txt"
//
// // Check if the log file exists, and create it if it doesn't
// if !fileManager.fileExists(atPath: logFilePath) {
// if !fileManager.createFile(atPath: logFilePath, contents: nil, attributes: nil) {
// printOS("Failed to create the log file.")
// return
// }
// }
//
// do {
// if let fileHandle = FileHandle(forWritingAtPath: logFilePath) {
// let ns = "\(string)\n"
// fileHandle.seekToEndOfFile()
// fileHandle.write(ns.data(using: .utf8)!)
// fileHandle.closeFile()
// } else {
// printOS("Error opening file for appending")
// }
// }
//}
// --- Load Plist file with launchctl ---
func launchctl(load: Bool, completion: @escaping () -> Void = {}) {
let cmd = load ? "load" : "unload"
@@ -749,22 +383,3 @@ func sendStartNotificationFW() {
func sendStopNotificationFW() {
DistributedNotificationCenter.default().postNotificationName(Notification.Name("Pearcleaner.StopFileWatcher"), object: nil, userInfo: nil, deliverImmediately: true)
}
//func getCurrentTimestamp() -> String {
// let dateFormatter = DateFormatter()
// dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
// return dateFormatter.string(from: Date())
//}
//func formattedDate(_ date: Date?) -> String {
// guard let date = date else { return "N/A" }
// let formatter = DateFormatter()
// formatter.dateStyle = .short
// formatter.timeStyle = .none
// return formatter.string(from: date)
//}