Swift / Cocoa: How to watch folder for changes?
Asked Answered
H

2

20

I'm writing a small macOS app, where I want to be able to watch a folder for changes. It doesn't need to watch subfolder, I only want to receive a notification if a file is added to the folder or removed.

It looks like NSFileCoordinator and/or NSFilePresenter could be used to achieve this, but I was not able to understand how to use them to achieve this.

Ideally this can be solved without having to include a third party framework.

Horticulture answered 20/5, 2018 at 21:43 Comment(0)
R
20

You can do this using NSFilePresenter. The observing class must conform to NSFilePresenter as shown below.

The presentedItemURL would point to the folder you want to observe. If there is a change in the folder presentedSubitemDidChangeAtURL get called. The code snipped below could give you an idea how it can work.

class ObservingClass: NSObject, NSFilePresenter {

    lazy var presentedItemOperationQueue = NSOperationQueue.mainQueue()
    var presentedItemURL:NSURL?
    

    func presentedSubitemDidChangeAtURL(url: NSURL) {
        let pathExtension = url.pathExtension    
        if pathExtension == "png"{
            refreshImages()
        }
    }

   func refreshImages(){
        let path = snapshotPath
        var isDirectory: ObjCBool = ObjCBool(false)
        
        if NSFileManager.defaultManager().fileExistsAtPath(path!, isDirectory: &isDirectory){
            if isDirectory{
                do {
                    let list = try NSFileManager.defaultManager().contentsOfDirectoryAtPath(path!) as Array<String>
                    for filePath in list {
                        if filePath.hasSuffix(".png"){
                            if let snapshot = snapshotAtPath(path! + "/" + filePath){
                                newSnapshotArray += [snapshot]
                            }
                        }
                    }
                } catch {
                    // error handling 
                }
            }
        }
    }
}

Best wishes.

Reinhard answered 21/5, 2018 at 7:16 Comment(6)
Thank you. This is perfect. :) I tried it, and it worked great. But one question. I also tried using presentedSubitemDidAppear(at:), in stead of presentedSubitemDidChangeAtURL(), since I'm actually only interested in when items are added to folder. But this method never gets called. Only the didChange. Do you know why?Horticulture
I experimented a lot with NSFilePresenter and got the same experience that presentedSubitemDidAppear(at:) is never called. Maybe its related to the NSFileCoordinator implementation Apple did with Finder. Be also aware that in some cases presentedSubitemDidChangeAtURL is called multiple times (e.g. copy bundles, file renaming, ...).Reinhard
Thanks for the input. It's a bit weird that it's not working, but I will get it to work with presentedSubitemDidChangeAtURL(), only have to do more checks and stuff.Horticulture
@Horticulture It is a bug since at least 2013 that will probably never be fixed. presentedSubitemDidAppear and accommodatePresentedSubitemDeletion are never called. See e.g. https://mcmap.net/q/663482/-nsfilepresenter-presentedsubitemdidappearaturl-method-never-gets-calledDerina
Thanks. Maybe I'll file an extra bug report one day.Horticulture
According the documentation, NSFilePresenter does not detect all changes: "Your presenter objects are not notified about changes made directly using low-level read and write calls to the file. Only changes that go through a file coordinator result in notifications.".Kenward
S
8

Marc T's answer still works and seems to be the easiest solution for this.

To make it work I needed to add the following line (could be at the init() of ObservingClass):

NSFileCoordinator.addFilePresenter(self)
Susurrous answered 4/5, 2020 at 21:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.