Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to observe shared app groups file changed

I have file which is shared between my app and extensions: Write to file from extension:

func writeToFile()
{
    let file = "file.txt" 
    let text = "data" //just a text
    let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.ml.test.apps")!

        let fileURL = dir.appendingPathComponent(file)
        do {
            try text.write(to: fileURL, atomically: false, encoding: .utf8)
        }
        catch {/* error handling here */}

    }

Read from the app:

func readFromFile()
{
    let file = "file.txt"
    let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.ml.test.apps")!

        let fileURL = dir.appendingPathComponent(file)
        do {
            let text2 = try String(contentsOf: fileURL, encoding: .utf8)
            NSLog("Dan: \(text2)")
        }
        catch {/* error handling here */}
    }

My question is how can I observe changes to this file. In case the extension writes to it and changes data so the app will get notification change and read the file.

like image 858
Dim Avatar asked Oct 17 '25 19:10

Dim


1 Answers

Here is a simple demo of approach based on usage NSFileCoordinator/NSFilePresenter pattern.

Tested with Xcode 11.4 / iOS 13.4

  1. Application part. Here is a ViewController plays a file presenter role, for simplicity (if one controller can manage many files, then it is better to create explicit presenters per-file)
class ViewController: UIViewController, NSFilePresenter {
    var presentedItemURL: URL?
    var presentedItemOperationQueue: OperationQueue = OperationQueue.main


    @IBOutlet weak var userNameField: UILabel!

    func presentedItemDidChange() { // posted on changed existed file only
        readFromFile()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // register for presentedItemDidChange work 
        NSFileCoordinator.addFilePresenter(self) 
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        // unregister - required !!
        NSFileCoordinator.removeFilePresenter(self) 
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let file = "file.txt"
        let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.test.apps")!
        presentedItemURL = dir.appendingPathComponent(file)

        readFromFile() // read previously stored data
    }

    private func readFromFile()
    {
        let coordinator = NSFileCoordinator(filePresenter: self)
        coordinator.coordinate(readingItemAt: presentedItemURL!, options: [], error: nil) { url in
            if let text2 = try? String(contentsOf: url, encoding: .utf8) {
                userNameField.text = text2 // demo label in view for test
            } else {
                userNameField.text = "<no text>"
                //just initial creation of file needed to observe following changes
                coordinator.coordinate(writingItemAt: presentedItemURL!, options: .forReplacing, error: nil) { url in
                    do {
                        try "".write(to: url, atomically: false, encoding: .utf8)
                    }
                    catch { print("writing failed") }
                }
            }
        }
    }
}
  1. Extension part (simple Today extension for demo having one button)
class TodayViewController: UIViewController, NCWidgetProviding, NSFilePresenter {
    var presentedItemURL: URL?
    var presentedItemOperationQueue: OperationQueue = OperationQueue.main

    override func viewDidLoad() {
        super.viewDidLoad()

        let file = "file.txt"
        let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.test.apps")!
        presentedItemURL = dir.appendingPathComponent(file)
    }
        
    @IBAction func post(_ sender: Any) { // action on button in extension
        writeToFile()
    }

    func writeToFile()
    {
        let text = "new data" //just a text
        let coordinator = NSFileCoordinator(filePresenter: self)
        coordinator.coordinate(writingItemAt: presentedItemURL!, options: .forReplacing, error: nil) { url in
            do {
                try text.write(to: url, atomically: false, encoding: .utf8)
            }
            catch { print("writing failed") }
        }
    }

    func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {
        completionHandler(NCUpdateResult.newData)
    }
    
}
like image 157
Asperi Avatar answered Oct 20 '25 10:10

Asperi