From 2712da73047a8c1f88ef865fc5faa3e51fb4207c Mon Sep 17 00:00:00 2001 From: NanoSector Date: Sat, 13 Mar 2021 20:07:19 +0100 Subject: [PATCH 1/2] Deleted -> fileEvent --- Brewed/Helpers/FileMonitor.swift | 6 +++--- Brewed/Model/ManagedServices.swift | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Brewed/Helpers/FileMonitor.swift b/Brewed/Helpers/FileMonitor.swift index 4b213d0..9d95e12 100644 --- a/Brewed/Helpers/FileMonitor.swift +++ b/Brewed/Helpers/FileMonitor.swift @@ -11,7 +11,7 @@ import Foundation protocol FileMonitorDelegate: AnyObject { - func deleted(url: URL, event: DispatchSource.FileSystemEvent) + func fileEvent(url: URL, event: DispatchSource.FileSystemEvent) } final class FileMonitor { @@ -28,13 +28,13 @@ final class FileMonitor { source = DispatchSource.makeFileSystemObjectSource( fileDescriptor: fileHandle.fileDescriptor, - eventMask: .delete, + eventMask: [.write, .delete], queue: DispatchQueue.main ) source.setEventHandler { let event = self.source.data - self.delegate?.deleted(url: url, event: event) + self.delegate?.fileEvent(url: url, event: event) } source.setCancelHandler { diff --git a/Brewed/Model/ManagedServices.swift b/Brewed/Model/ManagedServices.swift index e28fb4a..d71fd7f 100644 --- a/Brewed/Model/ManagedServices.swift +++ b/Brewed/Model/ManagedServices.swift @@ -44,7 +44,7 @@ class ManagedServices: ObservableObject, FileMonitorDelegate { }.cauterize() } - func deleted(url: URL, event: DispatchSource.FileSystemEvent) { + func fileEvent(url: URL, event: DispatchSource.FileSystemEvent) { DispatchQueue.main.async { self.refresh() } From 1cee8a4a226291228dcf31da4cb8df079e62126f Mon Sep 17 00:00:00 2001 From: NanoSector Date: Sat, 13 Mar 2021 20:54:53 +0100 Subject: [PATCH 2/2] Add FolderMonitor for launch directories --- Brewed.xcodeproj/project.pbxproj | 4 ++ Brewed/Helpers/FileMonitor.swift | 2 +- Brewed/Helpers/FolderMonitor.swift | 60 +++++++++++++++++++ .../Homebrew/Services/AutostartService.swift | 16 +++++ Brewed/Model/ManagedServices.swift | 25 +++++++- 5 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 Brewed/Helpers/FolderMonitor.swift diff --git a/Brewed.xcodeproj/project.pbxproj b/Brewed.xcodeproj/project.pbxproj index 2a81480..7ee7f22 100644 --- a/Brewed.xcodeproj/project.pbxproj +++ b/Brewed.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ CD6C478525FCF9E8004BA6DD /* RestartCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6C478425FCF9E8004BA6DD /* RestartCommand.swift */; }; CD6C478825FD0522004BA6DD /* GlobalAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6C478725FD0522004BA6DD /* GlobalAlert.swift */; }; CD6C478B25FD0B4F004BA6DD /* FileMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6C478A25FD0B4F004BA6DD /* FileMonitor.swift */; }; + CD6C479525FD4721004BA6DD /* FolderMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6C479425FD4721004BA6DD /* FolderMonitor.swift */; }; CD85838725FABE8E00E3974F /* BrewedApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD85838625FABE8E00E3974F /* BrewedApp.swift */; }; CD85838925FABE8E00E3974F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD85838825FABE8E00E3974F /* ContentView.swift */; }; CD85838B25FABE9300E3974F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD85838A25FABE9300E3974F /* Assets.xcassets */; }; @@ -38,6 +39,7 @@ CD6C478425FCF9E8004BA6DD /* RestartCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestartCommand.swift; sourceTree = ""; }; CD6C478725FD0522004BA6DD /* GlobalAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalAlert.swift; sourceTree = ""; }; CD6C478A25FD0B4F004BA6DD /* FileMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileMonitor.swift; sourceTree = ""; }; + CD6C479425FD4721004BA6DD /* FolderMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderMonitor.swift; sourceTree = ""; }; CD85838325FABE8E00E3974F /* Brewed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Brewed.app; sourceTree = BUILT_PRODUCTS_DIR; }; CD85838625FABE8E00E3974F /* BrewedApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewedApp.swift; sourceTree = ""; }; CD85838825FABE8E00E3974F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -186,6 +188,7 @@ CDCCCE4925FBED2C00C3590A /* Collection.swift */, CDA7929025FBFBB3006FFAB1 /* Regex.swift */, CD6C478A25FD0B4F004BA6DD /* FileMonitor.swift */, + CD6C479425FD4721004BA6DD /* FolderMonitor.swift */, ); path = Helpers; sourceTree = ""; @@ -269,6 +272,7 @@ CDA7928D25FBFA58006FFAB1 /* AutostartService.swift in Sources */, CD6C478525FCF9E8004BA6DD /* RestartCommand.swift in Sources */, CD85838725FABE8E00E3974F /* BrewedApp.swift in Sources */, + CD6C479525FD4721004BA6DD /* FolderMonitor.swift in Sources */, CDA7928725FBEFC9006FFAB1 /* ManagedServices.swift in Sources */, CD6C477125FCE2AC004BA6DD /* ServiceInfo.swift in Sources */, CDCCCE4725FBE6D500C3590A /* Service.swift in Sources */, diff --git a/Brewed/Helpers/FileMonitor.swift b/Brewed/Helpers/FileMonitor.swift index 9d95e12..d5b2df4 100644 --- a/Brewed/Helpers/FileMonitor.swift +++ b/Brewed/Helpers/FileMonitor.swift @@ -14,7 +14,7 @@ protocol FileMonitorDelegate: AnyObject { func fileEvent(url: URL, event: DispatchSource.FileSystemEvent) } -final class FileMonitor { +final class FileMonitor: FilesystemMonitor { let url: URL let fileHandle: FileHandle diff --git a/Brewed/Helpers/FolderMonitor.swift b/Brewed/Helpers/FolderMonitor.swift new file mode 100644 index 0000000..ad0ed95 --- /dev/null +++ b/Brewed/Helpers/FolderMonitor.swift @@ -0,0 +1,60 @@ +// +// FolderMonitor.swift +// Brewed +// +// Created by Rick Kerkhof on 13/03/2021. +// +// Code found from https://swiftrocks.com/dispatchsource-detecting-changes-in-files-and-folders-in-swift +// and altered. +// + +import Foundation + +protocol FolderMonitorDelegate: AnyObject { + func folderEvent(url: URL, event: DispatchSource.FileSystemEvent, additions: [URL]) +} + +protocol FilesystemMonitor {} + +final class FolderMonitor: FilesystemMonitor { + let url: URL + + let fileHandle: Int32 + let source: DispatchSourceFileSystemObject + + weak var delegate: FolderMonitorDelegate? + + init(url: URL) throws { + self.url = url + fileHandle = open(url.path, O_EVTONLY) + + source = DispatchSource.makeFileSystemObjectSource( + fileDescriptor: fileHandle, + eventMask: .write, + queue: DispatchQueue.main + ) + + var contents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil) + + source.setEventHandler { + let event = self.source.data + let oldContents = contents + contents = try! FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil) + self.delegate?.folderEvent( + url: url, + event: event, + additions: contents.filter { !oldContents.contains($0) } + ) + } + + source.setCancelHandler { + close(self.fileHandle) + } + + source.resume() + } + + deinit { + source.cancel() + } +} diff --git a/Brewed/Homebrew/Services/AutostartService.swift b/Brewed/Homebrew/Services/AutostartService.swift index 2b5229a..ae25e93 100644 --- a/Brewed/Homebrew/Services/AutostartService.swift +++ b/Brewed/Homebrew/Services/AutostartService.swift @@ -20,6 +20,18 @@ struct AutostartDirectory { return regex?.matches(url?.absoluteString ?? "") ?? false } + + static func urls() -> [URL] { + var urls = [ + URL(fileURLWithPath: AutostartDirectory.global, isDirectory: true) + ] + + if let local = try? FileManager.default.url(for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, create: false) { + urls.append(local.appendingPathComponent("LaunchAgents")) + } + + return urls + } } extension Service { @@ -30,4 +42,8 @@ extension Service { var startsAtLogin: Bool { AutostartDirectory.isLocalPlist(plist) } + + var plistName: String { + "homebrew.mxcl.\(self.id).plist" + } } diff --git a/Brewed/Model/ManagedServices.swift b/Brewed/Model/ManagedServices.swift index d71fd7f..6f6745f 100644 --- a/Brewed/Model/ManagedServices.swift +++ b/Brewed/Model/ManagedServices.swift @@ -7,11 +7,10 @@ import Foundation -class ManagedServices: ObservableObject, FileMonitorDelegate { - +class ManagedServices: ObservableObject, FileMonitorDelegate, FolderMonitorDelegate { @Published var services: [Service] = [] - private var monitors: [URL: FileMonitor] = [:] + private var monitors: [URL: FilesystemMonitor] = [:] func update(service: Service) { var services = self.services @@ -31,6 +30,14 @@ class ManagedServices: ObservableObject, FileMonitorDelegate { self.monitors.removeAll() self.services = services + + AutostartDirectory.urls().forEach { url in + if let monitor = try? FolderMonitor(url: url) { + self.monitors[url] = monitor + monitor.delegate = self + } + } + self.services.forEach { service in guard let plist = service.plist else { return @@ -49,4 +56,16 @@ class ManagedServices: ObservableObject, FileMonitorDelegate { self.refresh() } } + + func folderEvent(url: URL, event: DispatchSource.FileSystemEvent, additions: [URL]) { + let wantedPlistNames = services.map { $0.plistName } + + if !additions.contains(where: { wantedPlistNames.contains($0.lastPathComponent) }) { + return + } + + DispatchQueue.main.async { + self.refresh() + } + } }