0

Is it possible to reload/update a viewController's view once a file gets saved in the documents folder? If yes, kindly suggest how to accomplish this.

I'm trying to save a file in AppDelegate, but as it involves XML parsing, it is saved only when a view gets loaded. This is the view that is supposed to show the contents of the file. But as it gets loaded before the file is saved, the view doesn't display anything.

This query is related to my question at this link

Shall I share the code here again?

Prashant
  • 336
  • 3
  • 18
  • what is exactly has to be overloaded? – Vladyslav Zavalykhatko Jun 15 '17 at 07:47
  • I'm saving a file in AppDelegate and then a viewController is loaded, I'm checking if the file is present, if _YES_ load the file's contents into the view. Else display a message that there is nothing to display. But when I debug the app, I discovered that my method in AppDelegate is creating and saving the file after the view has been loaded and displayed, and the view displays a _nothing to show message_ . Hope I'm able to explain my problem. I'm not overloading anything. – Prashant Jun 15 '17 at 08:20
  • *load the file's contents into the view* <- do you mean load its content into the labels, textView etc.? – Vladyslav Zavalykhatko Jun 15 '17 at 08:31
  • yes. I'm trying to do this.But I'm only loading specific content. As the file has been XML parsed, it contains `NSDictionary` objects and I'm only loading the values of certain `keys` . – Prashant Jun 15 '17 at 08:46

1 Answers1

1

I can guide you through the process. What you need to implement is:

  1. When your file gets written to the storage, you can post a Notification through NotificationCenter. (Additionally you can check for the existence of the file immediately before posting the Notification).
  2. Add an observer for listening to that Notification through NotificationCenter in your ViewController (maybe inside viewDidLoad()).
  3. When you add the observer you need to specify a selector(method) which will be called when the notification occurs. So add a method and provide it as the selector for addObserver.
  4. Do your required operations in that method and at the last of the method mark your view dirty. It is usually done with self.view.setNeedsDisplay(). This will do the reloading/updating part.

EDIT: added some code example

Here I'm posting relevant code. Focus on the comments inside the code.

AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

    postNotificationAfterFileSave()
    return true
}

func postNotificationAfterFileSave() {
    // do your job with your custom class's method for saving
    // you should get a filepath back
    CustomXMLParser().parseAndSave { (filePath) in
        if FileManager.default.fileExists(atPath: filePath) {
            // if the file exists, post Notification
            NotificationCenter.default.post(name: Notification.Name("FileWrittenCompletion"), object: nil)
        }
    }
}

ViewController.swift

@IBOutlet weak var myLabel: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()
    // add an observer for your Notification and specify a selector that will be called when Notification is got
    NotificationCenter.default.addObserver(self, selector: #selector(updateUI), name: Notification.Name("FileWrittenCompletion"), object: nil)
    myLabel.text = "File is being saved"
}

@objc func updateUI() {
    // this method will be called when Notification happens
    // change your UI objects according to your needs
    myLabel.textColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
    myLabel.text = "File is saved successfully"
    // Finally mark your view `dirty` that will do the reloading task of the UI
    self.view.setNeedsDisplay()
}

CustomXMLParser.swift

func parseAndSave(completion: @escaping (String)-> Void ) {
    let when = DispatchTime.now() + 10
    DispatchQueue.global().asyncAfter(deadline: when) {
        let string = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
        // get the document directory
        let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("myFile").appendingPathExtension("txt")

        do {
            // write to filepath
            try string.write(to: fileURL!, atomically: true, encoding: .utf8)

            DispatchQueue.main.async {
                // return the filePath through completion block
                completion((fileURL?.path)!)
            }

        } catch {
            print("\(error.localizedDescription)")
        }

    }
}
nayem
  • 7,285
  • 1
  • 33
  • 51
  • Thanks @nayem , have already tried it. Didn't work. I appreciate your help. – Prashant Jun 15 '17 at 08:43
  • Give some time, I'm trying to implement a demo for this. – nayem Jun 15 '17 at 08:50
  • Sure. I'll wait. – Prashant Jun 15 '17 at 09:00
  • @Prashant, Hey I've done the demo but in `Swift`. Will it be a problem? And should I post only the relevant code here or should I upload the whole project somewhere else? – nayem Jun 15 '17 at 09:19
  • Sure, no problem you could post it here. Or upload it on some file hosting website and post a link, whatever you feel like. – Prashant Jun 15 '17 at 09:19
  • I too have tried this approach, but as I'm using background tasks, for completing XML parsing, via a custom class meant for parsing XML. It would be wrong to include a code for posting notification in that class. I appreciate your effforts. – Prashant Jun 15 '17 at 09:35
  • No you don't have to include the code for posting `Notification` in that class. You just have to get the main queue where you have called your custom class's method (I believe you mentioned your AppDelegate) . And inside the main queue, you just post the `Notification`. – nayem Jun 15 '17 at 09:39
  • Tried it earlier, will try again but with a slight modification. Thanks @nayem – Prashant Jun 15 '17 at 09:44
  • Thanks for all the help @nayem , but looks like this approach too isn't working for me, will keep tweaking my code and when I do get it right, will post it here. – Prashant Jun 15 '17 at 10:03
  • Okay. Do your parsing/saving method of your `CustomXMLParser` class have a completion block? Or do you have the permission to modify your `CustomXMLParser` class? – nayem Jun 15 '17 at 10:13
  • a completion block. – Prashant Jun 15 '17 at 10:43
  • Great. Can you modify the `completion block` to be executed on `Main Queue`? I've done another demo. – nayem Jun 15 '17 at 10:48
  • @Prashant You can find the source code [here](https://drive.google.com/open?id=0Bzxb70Gwq7HWVEZ1cTZNVFNsZGc) and I hope this will solve your problem. – nayem Jun 15 '17 at 11:07
  • Thanks a lot @nayem . I'll check. – Prashant Jun 15 '17 at 11:10
  • Hey, changed my approach altogether, that worked. Thanks for your help @nayem – Prashant Jun 16 '17 at 08:22
  • @Prashant If the approach was right and if that helped you sorting the problem out, you should mark this as accepted answer. That will help others to find out similar type issues. – nayem Jun 16 '17 at 08:32
  • Like I said, your approach and logic is definitely right. But I had to alter my entire approach to make code reusable, as my project is pretty big. But I'll accept your answer as this might help others – Prashant Jun 16 '17 at 09:31