-1

I am trying to simply save using NSDocument to a rtf. The code works fine but when I try to save to a view controller that isn't the initial child to the window controller it throws an error from a modal saying 'The document “” could not be saved as “”.'

How can I save the file to the Second View Controller?

             Window Controller
                    |
           Login View Controller

          |                |
SidebarViewContoller   ViewController1
                                 |
                                  TableViewController 2 Replaces VC1  
                                   Save TextView in this VC

I want to be able to write data into My NSDocument from the textView in ViewController2 and save it to the desktop Just like you would for instance in Pages

Here is the code

// Document.swift

class Document: NSDocument {

var text = NSAttributedString()
var documentViewController: DocumentViewController? {
    return windowControllers[0].contentViewController as? DocumentViewController
}


override init() {
    super.init()
    // Add your subclass-specific initialization here.
}

override class var autosavesInPlace: Bool {
    return true
}

override func makeWindowControllers() {
    // Returns the Storyboard that contains your Document window.
    let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
    let windowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as! NSWindowController
    self.addWindowController(windowController)
}

override func data(ofType typeName: String) throws -> Data {
    // Save the text view contents to disk
if let textView = documentViewController?.textView {
        let rangeLength = textView.string.count

    textView.breakUndoCoalescing()
    let textRange = NSRange(location: 0, length: rangeLength)
    if let contents = textView.rtf(from: textRange) {
            return contents
        }
    }
    throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}

override func read(from data: Data, ofType typeName: String) throws {
    if let contents = NSAttributedString(rtf: data, documentAttributes: nil) {
        text = contents
    }
}

//SecondViewController

override func viewDidAppear() {
    let document = self.view.window?.windowController?.document as!   Document
    textView.textStorage?.setAttributedString(document.text)
}
Cartisim
  • 116
  • 10
  • Update your question with your app's view controller hierarchy and what you want to happen. It sounds like you want to display a document's contents in a text view. Is that correct? – Swift Dev Journal Dec 27 '18 at 20:39
  • Yes I want to write the textAttributedString with NSDocument. In order to save rtf to the desktop – Cartisim Dec 27 '18 at 20:47
  • What does View Controller 1 contain and what does it do? You need to provide more details about the app as a whole to get help. It looks like you're trying to build upon [a tutorial I wrote to create a Mac rich text editor](https://meandmark.com/blog/2017/08/creating-a-document-based-mac-application-using-swift-and-storyboards/), but the tutorial is meant to work with one view controller. If you are using multiple view controllers or trying to create a master-detail interface, you are going to have make a bunch of changes to the code I wrote in the tutorial. – Swift Dev Journal Dec 27 '18 at 21:22
  • Okay. Yes, thanks for the tutorial I learned a lot from it. I was trying to keep it simple as possible because it is a complex app. Basically when the app launches it takes you to a login page. After logging in you are pushed to a masterSplitViewController., with the leftDetailView as a sideBarTableView used for navigation. I have a documents viewController that you segue to after selecting it from the sideBarTableView. This ViewController is the one where I need to write the data in the textView using NSDocument. I will adjust the hierarchy to reflect more clearly. – Cartisim Dec 27 '18 at 21:30

1 Answers1

0

The way you have things set up in your app, you are not going to be able to put an entire NSDocument's contents in a text view. Suppose you have 10 items in the table view and selecting an item fills the text view with some text. You're not going to be able to have a NSDocument for each of the 10 table view items inside a single document.

What you are going to have to do is create a data structure that represents a text file. Think of a chapter in a book or a scene in a screenplay. In your NSDocument subclass, you will have an array of these data structures. When you save the document, you will save it as a file wrapper, which is a directory of files that looks like a single file in the Finder. There will be one text file in the file wrapper for each item in the table view. Refer to the following article to learn more about file wrappers:

Working with File Wrappers in Swift

Now what you want to know is how to fill the text view when the table view selection changes. This is painful to do with Mac storyboards because the table view's controller and the text view's controller are separate scenes. Implement the delegate method tableViewSelectionDidChange in the view controller for the table view. Use the parent property to get the split view controller. Use the split view controller to get the text view's controller. Pass the selected row to the text view's controller and use that to get the right text file to display in the text view.

The split view controller should store a reference to the NSDocument. Use the parent property to access the split view controller from the text view's controller. With access to the split view controller, you can access the document to fill the text view with the contents of the text file corresponding to the selected item in the table view.

Swift Dev Journal
  • 19,282
  • 4
  • 56
  • 66