Files
Grapp/src/PathManager.swift

151 lines
4.0 KiB
Swift

import AppKit
struct Program {
var path: String = ""
var name: String = ""
var ext : String = ""
var img : NSImage? = nil
}
final class PathManager {
static let shared = PathManager()
private var dirMonitor: DirMonitor?
// NOTE: These are default paths where MacOS's default programs are
// stored. This list should be updated if something changes in newer
// MacOS versions.
static let defaultPaths = [
"/Applications",
"/System/Applications",
"/System/Applications/Utilities",
"/System/Library/CoreServices",
"/Applications/Xcode.app/Contents/Applications",
"/System/Library/CoreServices/Applications"
]
private(set) var paths: [String: [Program]] = [:]
private let fileManager = FileManager.default
private init() {
// UserDefaults.standard.removeObject(forKey: "programPaths")
if let dirs =
UserDefaults.standard.stringArray(forKey: "programPaths"),
!dirs.isEmpty
{
for dir in dirs {
addPath(dir)
}
} else {
for path in PathManager.defaultPaths {
addPath(path)
}
}
}
deinit {}
public func addPath(_ path: String) {
if isDirectory(path) {
paths[path] = []
}
}
public func removePath(_ path: String) {
paths.removeValue(forKey: path)
}
public func resetPaths() {
paths = [:]
for path in PathManager.defaultPaths {
addPath(path)
}
}
public func contains(_ name: String) -> Bool {
for path in paths {
for prog in path.value {
if prog.name == name {
return true
}
}
}
return false
}
public func refreshFilesystemWatchers() {
dirMonitor?.stop()
dirMonitor = nil
var buf: [String] = []
for path in paths {
buf.append(path.key)
}
dirMonitor =
DirMonitor(paths: buf,
queue: DispatchQueue.global(qos: .userInitiated))
// _ = dirMonitor!.start()
if dirMonitor!.start() {
print("Started monitoring directories.")
} else {
print("Failed to start monitoring directories.")
}
}
public func savePaths() {
var buf: [String] = []
for path in paths {
buf.append(path.key)
}
UserDefaults.standard.set(buf, forKey: "programPaths")
}
// PERF: Optimize some more. Do not rebuild the entire array, instead
// remove or add only needed programs. Thereby, limiting the amount of
// allocations.
public func rebuildIndex(at path: String) {
paths[path] = []
paths[path] = indexDirs(at: path, deepness: 2)
}
public func indexDirs(at path: String, deepness: Int) -> [Program] {
var array: [Program] = []
do {
var items = try fileManager.contentsOfDirectory(atPath: path)
items = items.filter({ isDirectory((path + "/" + $0)) })
for item in items {
let name = String(item.dropLast(4))
if item.hasSuffix(".app"), !contains(name) {
array.append(Program(path: path, name: name,
ext: ".app", img: nil))
}
if deepness > 0 {
array += indexDirs(at: path + "/" + item,
deepness: deepness-1)
}
}
} catch { print("Error: \(error.localizedDescription)") }
return array
}
public func updateIndex() {
print("updateIndex()")
for path in paths {
rebuildIndex(at: path.key)
}
refreshFilesystemWatchers()
}
// Touch paths to load them into CPU's cache for performance.
public func touchPaths() {
for path in paths {
_ = path
}
}
}