Document Based Application

Hi everybody,
Is there somebody out there who has a simple working project made as a Document based application (DBA)?

As a starting example I want to open a text-file edit it in a window and save it.
I just don’t get it! When I open an xcode template for a DBA I get a complete working application but where do I read the text file? Probably in the readFromData_ofType_error_(theData, typeName, outError) handler but where do assign the content of file to a NSTextView in the window that opens.
The same applies to the save I presume this happens in the handler dataOfType_error_(typeName, outError).

A lot of help wanted!

No responses :slight_smile: So I tried it partially to solve it myself…
So I’ll try to explain.

Create new project with the checkbox for Document Based Application checked

Goto the targets group in your project and double click DocBaseApplication
goto the properties tab
Because we are only reading unicode Text-files we will change the document types to extensions txt and OS Types to TEXT

Now fill in the code in the MyDocument.applescript…


script MyDocument
	property parent : class "NSDocument"
	
	property uiTextView : missing value --< Bound in IB from File's owner to a NSTextView in myDocuments.xib
	
	property fileContents : "" --< document default text (empty)
	
	on init()
		continue init()
		
		-- Add your subclass-specific initialization here.
		-- If an error occurs here, return missing value.
		
		return me
	end init
	
	on windowNibName()
		-- Override returning the nib file name of the document
		-- If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
		return "MyDocument"
	end windowNibName
	
	on windowControllerDidLoadNib_(aController)
		continue windowControllerDidLoadNib_(aController)
		-- Add any code here that needs to be executed once the windowController has loaded the document's window.
		--> Here we assign the text we read from the file to the TextView or in case of an empty document the default string
		if fileContents is not "" then uiTextView's setString_(fileContents)
		
	end windowControllerDidLoadNib_
	
	on dataOfType_error_(typeName, outError)
		-- Insert code here to write your document to data of the specified type. If the given outError is not missing value, ensure that you set contents of outError when returning missing value.		
		-- You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
		--> Here we are creating an NSData obkect from the TextView's string
		try
			set fileData to uiTextView's |string|'s dataUsingEncoding_(current application's NSUnicodeStringEncoding)
			set outError to missing value --< NO ERROR
		end try
		
		if outError is not missing value then
			set contents of outError to my NSError's errorWithDomain_code_userInfo_(my NSOSStatusErrorDomain, my unimpErr, missing value)
		end if
		
		return fileData --< Here we return de NSData we've created from the TextView's string
		
	end dataOfType_error_
	
	on readFromData_ofType_error_(mNSData, typeName, outError) --< Here we change data to a variable
		-- Insert code here to read your document from the given data of the specified type.  If the given outError is not missing value, ensure that you set contents of outError when returning false.		
		-- You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.
		--> Here we create a NSString from the NSData we've read from the file
		try
			set fileContents to current application's NSString's alloc()'s initWithData_encoding_(mNSData, current application's NSUnicodeStringEncoding)
			set outError to missing value --< NO ERROR
		end try
		
		if outError is not missing value then
			set contents of outError to my NSError's errorWithDomain_code_userInfo_(my NSOSStatusErrorDomain, my unimpErr, missing value)
		end if
		
		return true
		
	end readFromData_ofType_error_
	
end script

first add a property uiTextView that is bound to a NSTextView in IB on the MyDocuments.xib
add a property fileContents with an empty string

in the handler dataOfType_error_
you add the try block


try
	set fileData to uiTextView's |string|'s dataUsingEncoding_(current application's NSUnicodeStringEncoding)
	set outError to missing value --< NO ERROR
end try

and change the return to fileData


return fileData --< Here we return de NSData we've created from the TextView's string

in the handler readFromData_ofType_error_
you change the first parameter to mNSData (just the name of a variable)
you add the other try block


try
	set fileContents to current application's NSString's alloc()'s initWithData_encoding_(mNSData, current application's NSUnicodeStringEncoding)
	set outError to missing value --< NO ERROR
end try

And so you’re ready! Build and run.

But now I have another question!
How do I prevent a default empty document from opening?

Can the experts help me on that?

You need to create an application delegate class, and give it a handler like this:

on applicationShouldOpenUntitledFile_(sender)
 return no
end applicationShouldOpenUntitledFile_

Also, all that error stuff in the template is just window dressing; none of it actually works. And wrapping a method call in a try doesn’t do much; the usual failure mode is a result of missing value.

Shane,

Please tell me more :slight_smile:
I was already looking into this, but I don’t get it how to add a ApplicationDelegate.
Can you give me a page number in your fantastic book where I can find the way to add such a class.

I already created a new class but then I’m losing track… What do I have to do in IB?

Found it again!

Add a file to the project by clicking New File.
click on Cocoa class in the left panel and choose AppleScript Class Subclass of NSObject and click next give it a filename and click finish.

Now add the handler Shane supplied


on applicationShouldOpenUntitledFile_(sender)
	return no
end applicationShouldOpenUntitledFile_

Now about the linking in IB.
Set the library window to classes and goto a blue cube with the name of your just added class. Select it and drop it in the MainMenu.xib.
Now Control click on File’s owner and drag to the just added blue cube. Now you can select delegate.

Save the xib and go back xcode and run your code. Now your delegate is called and the method is called.

Luc,

Your document opening and saving works, but it assumes all you want to save is one string. You can actually save a series of variables, which might represent – well, whatever your document does. The only catch is that they must be classes property lists can handle: text, numbers, booleans, NSData, NSDates, and lists and records of the above.

Let’s say you have three such variables, x, y and z. Your handlers would be:

	on dataOfType_error_(typeName, outError)
		return current application's NSKeyedArchiver's archivedDataWithRootObject_({x, y, z})
	end dataOfType_error_

	on readFromData_ofType_error_(theData, typeName, outError)
		try
			set theResult to (current application's NSKeyedUnarchiver's unarchiveObjectWithData_(theData))
		        set {my x, my y, my z} to theResult as list
			return true
		on error errmess
			log errmess
			return false
		end try
	end readFromData_ofType_error_

Thanks Shane,

I will certainly keep this in mind.
But I will be processing xml :slight_smile: this time.