Have you ever carefully created a chart or graphic and then pasted it into a report only to discover you needed to change it later? Have you ever wished that software would work together so you could make this sort of change with just a few clicks? Well, this is now possible thanks to a new open source technology called LinkBack. Even better, you can add this technology to your own application today and join some of the growing list of Mac developers already supporting it. This article shows you how.
The best part about LinkBack is that it is fairly easy to add it to your own application; it may take as little as a few dozen lines of code. I'll demonstrate this by adding LinkBack to “Sketch”, a sample drawing application that comes with Apple's developer tools. By the end of this tutorial, you'll be able to create a graphic, paste it into Keynote or TextEdit, and then edit the graphic again with just a double click.
To work through this example (or just to play with the results), you will need a copy of the resource kit, available at http://www.linkbackproject.org/oreilly. The kit contains a version of the Sketch source code, a free plugin developed by King Chung Huang that adds LinkBack support to Keynote, a LinkBack-enabled version of TextEdit, and a finished version of the example application. It's fun and informative, so go get the kit now (and run the plugin installer if you have Keynote). Done? OK then, let's get started.
|
Related Reading
Cocoa in a Nutshell |
LinkBack is built on the trusty client and server model. The server is the application that creates the content in the first place and then edits it later. The client is the application that requests an edit from the server. For example, if you create a graphic and then paste it into a word processing document, the graphics application would be the server and the word processor the client.
Communication between client and server applications occurs primarily through OS X's pasteboard mechanism. LinkBack provides additional methods to handle anything that cannot be done with pasteboards alone. This means that most of the code you need to support LinkBack is already done in your Copy and Paste code; it just needs to be tweaked a bit.
There are five major steps to creating a LinkBack server application:
The rest of this tutorial will take you through these steps for the Sketch application.
The first step in supporting LinkBack is to include the special LinkBack data type along with your normal data types whenever you place content on the pasteboard. This data will be stored by client applications for later use when they request an edit.
In Sketch, this happens in SKTGraphicView's -copy: method, which is used for both the Copy and Cut operations. Modify this method with the highlighted changes shown in the code below:
- (IBAction)copy:(id)sender {
NSData* data ;
NSArray *orderedSelection = [self orderedSelectedGraphics];
if ([orderedSelection count] > 0) {
SKTDrawDocument *document = [self drawDocument];
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
[pboard declareTypes:[NSArray arrayWithObjects:
SKTDrawDocumentType, NSTIFFPboardType, NSPDFPboardType,
LinkBackPboardType, nil] owner:nil];
// save the pboard data for LinkBack.
data = [document drawDocumentDataForGraphics:
orderedSelection] ;
[pboard setPropertyList: [NSDictionary
linkBackDataWithServerName: @"sketch" appData: data]
forType: LinkBackPboardType] ;
// REST OF METHOD ...
}
}
The LinkBack data object you add to the pasteboard is a dictionary object. It contains a server name, which we will discuss in the next step, the application data that you provide, and some additional data provided by LinkBack.
The most important part of this is the application data you provide. When a client requests an edit, you will need to use this application data to display the content for editing. In the case of Sketch, we will simply write out the selected graphics in the format used for Sketch's private pasteboard type.
Run Sketch now, create a graphic, and paste it into a Keynote or TextEdit+LinkBack. Since the LinkBack data was included when you pasted it into the client, you can now double-click on the graphic and the client will try to request an edit. But you won't get an edit. You will get an error instead.

Why is this? Well, you haven't completed the next step of our tutorial yet.
To be notified when a client wants your application to edit some content, Sketch needs to register as a LinkBack server. This step involves two parts. The first part requires one line of code and the second part requires no code at all.
To received edit requests, you need to tell LinkBack that you are interested in receiving edit requests. You do this with a line of code that needs to be called shortly after your application launches. The best place to put this is in the -applicationDidFinishLaunching: method of your application delegate, which is exactly what we will do for Sketch. Add the line of code highlighted below to this method in the SKTDrawAppDelegate:
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
[LinkBack publishServerWithName: @"sketch" delegate: self] ;
[self showToolPaletteAction:self];
}
By now you've probably noticed a trend with this “sketch” server name thing. The server name is used to identify the part of your application that responds to edit messages. It is actually possible to have more than one LinkBack server in each application, though not very common. The point here is you need to pick a server name and stick with it throughout your application.
The Info.plist is a file found in your application bundle that contains information about your software. Mac OS X uses this information to find out what types of files your application can open, among other things. LinkBack uses this information to determine your level of LinkBack support. You need to add two special keys to this file:
LinkBackServer key is used to identify the server name you will register with when launched. LinkBack uses this method to find the server application to launch when a client requests an edit. For Sketch the value of this key is...“sketch”, of course.LinkBackApplicationURL key contains a link to your software's home page. If a user tries to edit some content created by your application but your application is not installed, LinkBack will offer to take them to the URL you provided so they can download your software.In the interest of keeping this tutorial short, the Sketch application in the resource kit already has these two keys added for you. If you add LinkBack to your own application, however, keep in mind that you will need to add these keys to your Info.plist as well.
That's it for step two. Now that you are listening for edit requests, you need to handle them. This is what we will do in step 3.
|
During this step you are going to modify the Sketch document class so you can initialize it with LinkBack data and then you will add the method to actually create documents when an edit request is received.
Remember the graphics data we generated back in step one? Now we're going to use that data to create a document with graphics for the user to edit. Most of this responsibility will fall to the Sketch document class, SKTDrawDocument.
To start with, change the SKTDrawDocument header file by adding the ivar and new method names shown below. These changes will be explained when we write the implementations of the methods.
@class LinkBack ;
@interface SKTDrawDocument : NSDocument {
@private
LinkBack* _link ;
NSMutableArray *_graphics;
}
- (id)initWithLinkBack:(LinkBack*)aLink ;
...REST OF INTERFACE DECLARATION...
By now you might be wondering exactly what this LinkBack object is anyway. Every time a client requests an edit from a server, a new LinkBack object is created. You use this object to get your application data, to notify the client of edits, and to close the connection when you are finished editing.
In the new init method we are adding, we will use the LinkBack object to obtain the application data so we can populate our document with graphics. Switch to the implementation of SKTDrawDocument and add the init method:
- (id)initWithLinkBack:(LinkBack*)aLink
{
if (self = [self init]) {
_link = [aLink retain] ;
[_link setRepresentedObject: self] ;
// get graphics from link
id linkBackData = [[_link pasteboard] propertyListForType:
LinkBackPboardType] ;
id graphics = LinkBackGetAppData(linkBackData) ;
graphics = [self drawDocumentDictionaryFromData: graphics] ;
graphics = [self graphicsFromDrawDocumentDictionary: graphics] ;
[self setGraphics: graphics] ;
// fix up undo
[[self undoManager] removeAllActions] ;
[self updateChangeCount: NSChangeCleared] ;
}
return self ;
}
There's one other change we want to make. When the user has a document window opening that is editing graphics from another application, we want them to know that, by setting the title of the window to reflect the source of the graphics. To do this, add the following method to the SKTDrawDocument implementation as well:
- (NSString*)displayName
{
if (_link) {
NSString* sourceName = [_link sourceName] ;
NSString* sourceAppName = [_link sourceApplicationName] ;
NSString* ret = [NSString stringWithFormat: @"Graphics from %@
(%@)", sourceName, sourceAppName] ;
return ret ;
} else return [super displayName] ;
}
Finally, we need to add the method that will actually get called by LinkBack whenever a client requests an edit. Switch to the SKTDrawAppDelegate implementation and add the following method:
- (void)linkBackClientDidRequestEdit:(LinkBack*)link
{
SKTDrawDocument* doc = [link representedObject] ;
// if no document already exists for this link, create one
if (nil==doc) {
NSDocumentController* dc = [NSDocumentController
sharedDocumentController] ;
doc = [[SKTDrawDocument alloc] initWithLinkBack: link] ;
[dc addDocument: doc] ;
[doc makeWindowControllers] ; // causes it to display.
}
// always bring app and document window to the front.
[NSApp activateIgnoringOtherApps: YES] ;
[doc showWindows] ;
}
Now, build and run your Sketch app, create a graphic, add it to your client application, and double-click again. A document window opens with the graphic. Now we're getting somewhere. But how do changes to your graphic make it back to the client? That's where step 4 comes in.

Changes are sent back to the client application whenever you save your special document in Sketch. Simply add the following method to the SKTDrawDocument implementation.
- (void)saveDocument:(id)sender
{
if (_link) {
NSArray *sel = [self graphics];
if ([sel count] > 0) {
NSPasteboard *pboard = [_link pasteboard];
id data ;
[pboard declareTypes:[NSArray
arrayWithObjects:SKTDrawDocumentType, NSTIFFPboardType,
NSPDFPboardType, LinkBackPboardType, nil] owner:nil];
data = [self drawDocumentDataForGraphics: sel] ;
[pboard setData: data forType: SKTDrawDocumentType];
[pboard setData:[self TIFFRepresentationForGraphics: sel]
forType:NSTIFFPboardType];
[pboard setData:[self PDFRepresentationForGraphics: sel]
forType:NSPDFPboardType];
// save the pboard data for LinkBack.
[pboard setPropertyList: [NSDictionary
linkBackDataWithServerName: @"sketch" appData: data] forType:
LinkBackPboardType] ;
[_link sendEdit] ;
// fix up undo
[[self undoManager] removeAllActions] ;
[self updateChangeCount: NSChangeCleared] ;
} else NSBeep() ;
} else [super saveDocument: sender] ;
}
For documents created from a LinkBack object, this method simply loads the LinkBack pasteboard with the contents of the document and invokes -sendEdit, which notifies the client of the changed content. Most of this code is straight out of the -copy: method we looked at earlier. In a properly factored application, you could probably reduce the code needed for this method even more.
OK, let's try it all again. Build and run, create a graphic, paste it into a your client app, double-click on the graphic, make some changes, and save. Presto! The graphic updates in the client application. Satisfying isn't it? All we have left is to tie up a few loose ends and we're done.
As nice as it is to edit content, at some point the user is going to finish editing. The user may close the document in the client or in the server. Either way, both applications need to be informed when the user finishes editing so they can close their windows. This step adds code to clean up after the document closes a document on either the server or client side.
When the user closes the Sketch document, we need to inform the client that we are finished editing. That way if the user tries to edit the content again, the client knows that it needs to request a new edit instead of trying to reuse the existing one.
To do this, add the following method to SKTDrawDocument:
- (void)closeLinkIfNeeded
{
if (_link) {
[_link setRepresentedObject: nil] ;
[_link closeLink] ;
[_link release] ;
_link = nil ;
}
}
- (void)close
{
[self closeLinkIfNeeded] ;
[super close] ;
}
Similarly, if the user quits the client application or closes a document, the client will notify Sketch if it has any open LinkBack sessions. This message is received by the object you registered to received edit requests in Step 2. Add the following method to the SKTDrawAppDelegate:
- (void)linkBackDidClose:(LinkBack*)link
{
// find document for live link. Close document.
SKTDrawDocument* doc = [link representedObject] ;
[doc close] ;
}
Congratulations, you're done. You now have a fully functioning LinkBack-enabled Sketch application.
Applications are more useful when users can use them together, which is what makes LinkBack so interesting. It's also easy to add to your own application, so you can get the network benefits of all the other applications that support LinkBack with little effort.
If you are interested in using LinkBack in your own application, have a look at the LinkBack Developer's Guide in the latest LinkBack Source Distribution available from the linkbackproject.org website. You can also join the mailing list to get answers and advice on how to best use LinkBack in your software. I hope to see you on the list soon.
Charles Jolley is the creator of the LinkBack and managing director at Nisus Software, makers of Nisus Writer Express, the first Cocoa word processor for Mac OS X.
Return to the Mac DevCenter.
Copyright © 2009 O'Reilly Media, Inc.