Cocoa

Cocoa network persistence

Persisting NSDictionary ‘documents’ into the multi–user CouchDB store across the network

Apache CouchDB is a distributed, fault-tolerant and schema-free document-oriented database. CouchDB does support data structures within the document but deep hierarchies are best served using a relational database.

Document elements are stored in a JSON representation within a document. There is a natural “impedance match” between JSON and Cocoa’s Property List objects with the exception of NSDate. Dates will need to be serialised into strings.

Collections of documents are grouped in Views, specified by Map Reduce functions written in Javascript.

CouchDB has a low barrier to entry. CouchDB’s most beneficial feature to Watershed’s situation now is the casual integration with all categories of applications and the ease with which sophisticated shared storage is possible from application resource–sensitive client environments such as Cocoa on the iPhone and Javascript applications.

NSMutableDictionary Category

Persistence is achieved in Cocoa by using NSMutableDictionary as the container for each document. Each document can be identified by the identifer and the revision, stored in the dictionary using the NSString keys _id and _rev. The documents are mutable to track revisions over time. The persistence is implemented using a category on NSMutableDictionary. We haven’t done much work here—just assembled the pieces Lego style. Most of the hard work is done by json-framework which will need to be included in the project.

The category, composing of two methods, is partially listed below:

  1. #define COUCHDB_ID_KEY @"_id"
  2. #define COUCHDB_REV_KEY @"_rev"
  3. #define HTTP_METHOD_POST @"POST"
  4. #define HTTP_METHOD_PUT @"PUT"
  5. @implementation NSMutableDictionary (NSDictionary_FBTFCouchDB)
  6. + (id)dictionaryFromDocumentStore:(NSURL *)storeURL withIdentifier:(NSString *)identifer {
  7. if (!storeURL || !identifer) return nil;
  8. NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:identifer relativeToURL:storeURL] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
  9. NSError *error = nil;
  10. NSURLResponse *response = nil;
  11. NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
  12. if (error) {
  13. NSLog(@"Network Connection Error: %@", [error localizedDescription]);
  14. return nil;
  15. }
  16. // Couch uses UTF8 encoding for transfers
  17. NSString *jsonString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
  18. if (!jsonString) return nil;
  19. error = nil;
  20. SBJSON *jsonParser = [[SBJSON alloc] init];
  21. id result = [jsonParser objectWithString:jsonString allowScalar:YES error:&error];
  22. [jsonString release];
  23. [jsonParser release];
  24. if (error) {
  25. NSLog(@"JSON Parse Error: %@", [error localizedDescription]);
  26. return nil;
  27. }
  28. return result;
  29. }
  30. - (BOOL)persistInDocumentStore:(NSURL *)storeURL {
  31. // We do both inserts and updates in this method
  32. NSString *methodName;
  33. NSURL *requestURL;
  34. if ([self objectForKey:COUCHDB_ID_KEY]) {
  35. // this dictionary originated from the document database so update by including ID in URL
  36. methodName = HTTP_METHOD_PUT;
  37. requestURL = [NSURL URLWithString:[self objectForKey:COUCHDB_ID_KEY] relativeToURL:storeURL];
  38. } else {
  39. // This is a new Dictionary so we are inserting
  40. methodName = HTTP_METHOD_POST;
  41. requestURL = storeURL;
  42. }
  43. NSMutableURLRequest *aRequest = [[NSMutableURLRequest alloc] initWithURL:requestURL];
  44. [aRequest setHTTPMethod:methodName];
  45. NSError *error = nil;
  46. SBJSON *jsonParser = [[SBJSON alloc] init];
  47. NSString *argsData = [jsonParser stringWithObject:self allowScalar:NO error:&error];
  48. if (error) {
  49. NSLog(@"JSON Serialisation Error: %@", [error localizedDescription]);
  50. return NO;
  51. }
  52. // Finish setting up the request and send to document store
  53. [aRequest setHTTPBody: [argsData dataUsingEncoding:NSUTF8StringEncoding]];
  54. NSURLResponse *response = nil;
  55. error = nil;
  56. NSData *responseData = [NSURLConnection sendSynchronousRequest:aRequest returningResponse:&response error:&error];
  57. [aRequest release];
  58. // Now parse the results
  59. NSString *result = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
  60. error = nil;
  61. NSDictionary *results = [jsonParser objectWithString:result allowScalar:NO error:&error];
  62. if (error || [results objectForKey:@"error"]) {
  63. // Something went wrong. report, clean up and return
  64. if (error) {
  65. NSLog(@"Can not parse results: %@", [error localizedDescription]);
  66. } else {
  67. NSLog(@"Database Error: %@", [results valueForKey:@"reason"]);
  68. }
  69. [jsonParser release];
  70. [result release];
  71. return NO;
  72. } else {
  73. // update the revision and the id (in case this is an insertion we do both)
  74. [self setValue:[results valueForKey:@"id"] forKey:COUCHDB_ID_KEY];
  75. [self setValue:[results valueForKey:@"rev"] forKey:COUCHDB_REV_KEY];
  76. [jsonParser release];
  77. [result release];
  78. return YES;
  79. }
  80. }
  81. @end

Other Possibilities & Further Development

The next step will be creating a database co–ordinator or connection proxy to manage persistence in a more sophisticated fashion. This can support features such as authentication & authorisation, revisions, rollback & merging of documents and the efficient storage & retrieval of collections.

Whilst CouchDB is not an object database and doesn’t encourage deep hierarchies, we’ve experimented recently combining CouchDB documents with Core Data’s NSManagedObjects by keeping document identifiers in managed objects and merging at display time. In the other direction, NSManagedObjects’ snapshots can easily be included in documents in the store when the NSManagedObject is saved in the context.

CouchDB is a peer-based distributed database system, it allows for users and servers to access and update the same shared data while disconnected and then bi-directionally replicate those changes later. We’d like to look into the possibility of using Couch, embedded in the desktop application bundle, locally and then allow CouchDB to replicate and sync between different instances as they appear on the network.

|

Replacing Cocoa Persistence Framework

Taking Watershed's Cocoa desktop application to relational database communications forward

Currently…

Watershed's Communications staff use an in-house desktop application, named Communicate, to edit and control "what's on" information for the organisation. Communicate uses a Cocoa persistence framework, I wrote some years ago in Objective-C, to fetch and save objects into an Openbase relational database. The framework uses plug-ins to directly connect to and communicate with databases from different vendors.

An Objective-C, Cocoa, business objects framework is written on top of the persistence classes which describes and implements all the business logic. This framework is a copy of a server-side, WebObjects, Java framework.

Network performance is good, especially as most users use the applications over a LAN or local wireless connection. As the size of the database has grown, unarchiving large streams of data into the object graph is beginning to use a significant amount of time.

The persistence framework was created, over five and a half years ago, in 2003. New technologies used to develop desktop applications have become available. The persistence framework uses its own classes for sorting and qualifiing object fetch requests. For instance, we could reduce our maintenance footprint by adopting NSSortDescriptor and NSPredicate.

Aims & Objectives

A new persistence framework must give Watershed database independence. We would like to move from Openbase to MySQL some point in the medium term. The flexability is currently provided by database specific plug-ins and we would like to change this to use a three tier architecture for desktop client applications.

Using a set of desktop frameworks, written in Objective-C, mirroring a set of server frameworks, written in Java, means defining and implementing any object relationships, validation rules and convenience routines twice. It's difficult to create comparable classes using Java and Objective-C and it's obviously twice the work and twice the maintenance. A new framework should help alleviate this duplication.

Core Data is Apple's object life-cycle and persistence framework. Using Core Data has advantages, such as automatic undo, and moving forward will gain new features such as possible automatic AppleScript access to the model. Core Data has a natural synergy with bindings, KVC and KVO. NSManagedObject implements many techniques, including uniquing, identification and change tracking, used in a persistence framework. It would be sensible for a new persistence framework to take advantage of existing technology in Core Data.

Web applications, such as Basecamp, Lighthouse and soon Watershed, frequently provide a REST API to the services. Often the API sends and receives XML over an HTTP connection. This API can be used to build a wide range of software but is especially useful for desktop applications, widgets and iPhone applications communicating with the web service. A persistence framework would be all the more valuable if it helped us to communicate with these types of services.

Option One: Enhance Exitsting Framework

The existing persistence framework can still be used. There is nothing intrinsicaly wrong and we can write adapters for almost all networked, relational databases. The framework connects to the database and fetches the data it needs. This approach where the framework connects and authenticates directly to the database just feels wrong viewed alongside mordern techniques for over the internet delivery.

Some performance issues could be resolved by adding the ability to partially load business objects, similar to the way raw rows can be used in EOF. We suspect performance improvements could also be found in the routines that deal with the model lookups and the creation of Cocoa objects from huge streams of database data.

Option Two: FBTFPersistentObjects with Core Data In-Memory Stores

We have an object persistence framework that we have been using for the past five years. We could leverage some of the existing code by moving the framework onto Core Data and modernising the interfaces. Using an in-memory store and the notifications we could replicate the functionality we currently have. Moving our base business object to a subclass of NSManagedObject we can reuse many of the change tracking functionality built into NSManagedObject to store our objects into a network RDBMS.

FBTFPersistentObjects uses built-in database adapters. For each database vendor, there will be an adapter. The database adapter subclasses manage SQL generation and network communication. The database adapter is not dynamically loaded but is compiled and linked into the framework therefore changing databases could require substantial work meaning no real world database independence.

Clients connecting directly to the database requires negotiating network security and authenticating users' requests on the database, loosing our independent user management. Business logic and validation written into the client can't be easily reused in other applications.

Option Three: Core Data REST Stores

The third option is to essentially use Core Data as a local cache for REST web services. Using a local SQLite store, each client would keep its own copy of the used portions of the server data. The managed object model would be interrogated to generate network request URLs and formulate request data. A mapping delegate or configuration file would also be available to replace the defaults on a project-by-project basis.

Using the delegate notifications from the managed object context, changes would be propagated through RESTful HTTP POST and PUT methods to URLs on the server. At this point objects can be updated, inserted and removed from the server store. Fetches are likely to be performed against the server if a network connection is available and the local cache updated. Data schema formats could include XML, JSON or Binary Property Lists.

By using REST stores we're complementing the work we have been doing to add RESTful request handing to WebObjects applications and would be useful when writing desktop clients for the upcoming changes in dShed.net as well as the ever increasing number of web applications with REST APIs.

To be a truly effecient companion to REST web services, NSManagedObjects should be capable of sub-atomic operations. We would need to find out if managed objects could be made to support property values that can be trickled in as they are needed, in a similar fashion to the way relationships are faulted in.

Other Considerations

iPhone

Working closely with Core Data prohibits using the persistence framework with iPhone client applications. With the limited memory and performance available on the iPhone, I suspect it would be advantageous to create a separate, simpler, resource saving mapping routines that deserialze XML or binary Property LIsts.

WebObjects REST Handler

We've been using WebObjects' frameworks to create web applications for some time now and have recently been working on a method to quickly and effortlessly add RESTful interfaces to applictions.

The REST interface over HTTP was designed, not only to provide a publicy available API, but also as low configurtion data provider to a desktop persistence framework.

Ruby on Rails

Whilst, not heavy user of Ruby on Rails, it's impressive how simply and amazingly quickly a web application can be developed to vend XML, or JSON, data for a desktop client. These applications use type hints in the attribrutes of the XML and expect XML elements to be named to a convention. Any Cocoa persistence framework developed should be able to work closely, and cleanly, with a generated scaffolded Rails application with little configuration.

|

Cocoa EOF? No thanks

As our current persistent framework at Watershed needs work I’ve been thinking about shared persistent data frameworks

It appears to be with increasing frequency that one can hear experienced Cocoa developers reminiscing about EOF for desktop applications and new developers wondering why Core Data doesn’t use stores in network RDBMS’s from a range of vendors.

As Watershed’s persistence framework will shortly be replaced, I’ve been working on some possible replacements. One of the best things about being an in-house developer is, I get to to see people using my applications every day. I walk past people’s desks and see which windows are open and what tasks users perform. An incredibly frequent task runs as follows:

  1. The user needs to make a quick edit to an item stored in a relational database.
  2. The user opens their Cocoa client and a window opens, normally containing a NSTableView listing all the entries.
  3. The persistence routines generate the required SQL, sends it to the database and receives a stream of data.
  4. The persistence routines turn the data into a object graph generating thousands and thousands of objects. Each business object, even with relationships represented by proxy objects, contains many attributes each of which is represented by an object.
  5. Once the persistence routines have finished creating the thousands and thousands of objects, making copies & caches and linking them together by setting object instance variables: the user opens the first item in the list, corrects a typo and quits the application.

The problems evident in the example could be seen as a UI issue. Possibly the way forward is to follow iPhone applications’ methods of display a small number of entries then prompting the user to load then next batch. iTunes displays thousands of tracks in a table on launch so why can’t my applications do the same? The business object implemented using NSManagedObject or an EOGenericRecord is an atomic unit. Why does the application need to populate an entire business object just to display the title in one column?

Caching large chunks of data on the client made efficiency enhancements that were just not needed. Launch times are incredibly slow, damaging the user experince. Worried about excess network traffic, we started off designing clients with aggressive caching. Our controllers in the Cocoa applications now save changes more frequently, and with less modality in the edits. This results in a constant trickle of data across the network but a much smoother user experience.

Public applications with a large number of users render bulky local caches ineffective. As the number of editors increases, the probability that a local cache is no longer synchronised increases.

Frameworks such as EOF or ActiveRecord work very well over high speed, high quality networks but many network connections for Macintosh, and iPhone, clients are mobile and made wirelessly or are intermittent. Many clients connect over fragile internet connections where the performance will destroy the user experience.

So, no I don’t want Apple to re-release EOF for Objective-C or allow Core Data stores to reside in a network RDBMS—I want something better.

|

Beamer

Broadcasting short films to mobile devices using Bluetooth in the Watershed Café Bar.

As part of 2007's Electric December we wanted to send video films to mobile phones quickly and cheaply. One method selected was to send the films directly to user's mobile phones using Bluetooth from inside the Watershed building. We were quoted approximately £2000 to enable this from a single access point for twenty four days. The 'transmitter module' would be shipped to us, we would install it securely in the building and supply it with an Internet connection. Set-up and Configuration would be done remotely. There wasn't a budget for this and we thought that Bluetooth broadcasting was a technology that would be useful for us to repeat so we allowed ourselves a few days to write the software.

We decided to write a desktop application (called Beamer). Strange when a daemon would perhaps have been a better choice but we hope one day to make the application an easy to use broadcasting application for any user with playlists and other features. We built a Cocoa application using Core Data to store discovered device lists and preferences. Bluetooth connectivity was handled using C functions and Objective-C methods in the IOBluetooth framework. The application was deployed on a Mac Mini (Core 2 Duo) running 10.4.10 for December 1st 2007.

Each day, a different film from Electric December is broadcast to mobile phone users in the foyer and Café Bar.

Statistics

The statistics listed are for the period December 1st - 24th, 2007.

Total number of devices detected: 2540. Although the Mac Mini was surrounded on most sides by copious amounts of tin foil to make the radio signal as directional as possible it often registered mobile phones from people walking along the quayside below or using the Chicago Rock bar. Beamer was only looking for phones, PDAs and similar mobile devices and this number does not include laptop and desktop computers.

Device names not registered: 972. Some users moved so quickly through the space that they were out of range before Beamer had a chance to ask the device for its name. For the first half of December the films were broadcast from Watershed's foyer, above the Box Office, but we noticed that apart from the periods when visitors queue for the cinemas in the evenings, a space with a large turnover of transient users is not ideal location. For the second half of the transmission period the Mac Mini was located in the Café Bar, below the spirits' rack, and this increased the number of names retrieved and connections attempted.

Number of users refusing a film: 104. Beamer would attempt to send the film to a user' mobile twice. If a device rejected the offer of a film twice it was never asked again for the duration of Electric December.

Number of films successfully transferred: 65. The films were transferred to 57 different devices so a small number of users, mostly staff, received more than one film.

Number of devices connection attempted: 1077. Beamer attempted to connect to roughly two thirds of the mobile devices that stayed around long enough to get the display name but only managed a connection in less than 20% of cases. There are many possible reasons for this and some in this broadcast include:

  • A connection was initiated but the owner frequently failed to notice the notification on the screen.
  • The mobile device did not support the transfer protocols needed.
  • Each film was around 1MB and many devices do not have enough storage to hold files of this size.

Daily Distribution

daily_graph

The number of films transferred roughly parallels the pattern shown by the Electric December web site. The largest numbers are recorded when interest in the project is high at the start of the month and gradually decreases over the month until just before Christmas when the project is forgotten.

In the early days of the deployment, kernel panics were frequent and and Beamer only managed to run for a number of hours at a time. Days two and five were days when the application was only running for a small number of hours. From the graph it can be clearly see that, unfortunately, some days Beamer didn't run at all. For the first part of the deployment, a kernel panic had locked up the machine and it needed to be restarted. Later in the deployment, the mains electrical supply was not turned on and the Mac Mini failed to start.

Future Bluetooth Applications

The number of films transferred, although not huge, is a promising start. Downtime was approximately 25% so the total number of films distributed could be improved upon easily. As well as future short film distribution, Bluetooth could also be used to distribute other information to users of the Watershed such as contact or screening information. Having written that, I'm not convinced that broadcasting, unannounced content out to mobile phones will ever be that  successful on a large scale. I'm hoping at some point in the near future, to produce a 'kiosk' type viewer with user initiated wireless transfers to mobile phones and devices. Having the user send a film to their own device is far less intrusive and perhaps would be used widely.

|

Multi-Channel Audio

Delivering forty channel audio using QuickTime APIs to adjust movie files.

A little while ago, Oliver (Watershed's ICT Co-ordinator) , asked me to write a small command line tool to set the audio output channels of a QuickTime movie. Oliver was writing software for Dream Director, a project that plays audio to people sleeping in pods in response to rapid eye movement.

A Mac mini plays back the audio to up to twenty pods (containing sleeping people) through a collection of M-Audio Firewire410 boxes. As the audio-out hardware units have been aggregated into a single virtual device with forty available channels, it was just a case of playing back a stereo file through a selected pair of channels.

QuickTime movies will save the audio channel mapping inside the file, so the requirement was to build a tool that would set the selected channels in a movie and save the file to disk. The source listing is for a Cocoa command-line Tool, using QTKit, to open an audio file, set the track mapping and save as a QuickTime movie.

QuickTime now uses Core Audio to handle the audio channel mapping and I thought this may be of some use to people. The partial listing below shows the code needed to map audio channels using QuickTime and a link to the source file of the tool is below the listing. Sorry about the wrapped source listing.

  1. Movie qtMovie = [movie quickTimeMovie];
  2. Track audioTrack = GetMovieIndTrack(qtMovie,1);
  3. AudioChannelLayout* trackChannelLayout = NULL;
  4. OSStatus err = noErr;
  5. UInt32 trackChannelLayoutSize;
  6. // Allocate a layout of the required size
  7. trackChannelLayoutSize = fieldOffset(AudioChannelLayout, mChannelDescriptions[2]);
  8. trackChannelLayout = (AudioChannelLayout*)calloc(1, trackChannelLayoutSize);
  9. trackChannelLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
  10. trackChannelLayout->mNumberChannelDescriptions = 2;
  11. // Adjust the channel Assignment so that for an index we get a pair of channel
  12. // allocations. The index starts at 1
  13. // index channelA channelB
  14. // -----------------------------
  15. // 1 discreet0 discreet1
  16. // 2 discreet2 discreet3
  17. // 3 discreet4 discreet5
  18. // 4 discreet6 discreet7
  19. // ... and so on
  20. channelAssignment = (channelAssignment-1)*2;
  21. #warning This will break if Apple change the CoreAudio's Channel Layout internals
  22. trackChannelLayout->mChannelDescriptions[0].mChannelLabel = (1L<<16) | channelAssignment;
  23. trackChannelLayout->mChannelDescriptions[1].mChannelLabel = (1L<<16) | (channelAssignment + 1);
  24. // Set the track layout
  25. err = QTSetTrackProperty(audioTrack, kQTPropertyClass_Audio, kQTAudioPropertyID_ChannelLayout, trackChannelLayoutSize, trackChannelLayout);
  26. if (err != noErr) {
  27. NSLog(@"**Error** QuickTime SetPropertyError: %i", err);
  28. return 1;
  29. }
  30. Download the full source code.

    |

Communicate

This is essentially a glossary entry for Communicate. I often write about Communicate's development in the blog and thought a quick overview would be useful.

Communicate is a desktop application that Watershed staff use to administer the data that is used in the creation of the Web site. Communicate is also used to control display aspects of the web site. Communicate runs on Apple Macintosh computers running MacOS X 10.3 or later and is written in Cocoa by Watershed's Communications department.

Watershed's Web site contains constantly changing content and it was decided to build a desktop application to administer the data. A desktop application has many advantages of a web application, especially in 2002 when there was no AJAX, widget toolkits, Javascript libraries… etc. A desktop application has speed, ease of use and a familiar interface. The disadvantages include platform dependence, version concurrency and development time. Work began on Communicate at the beginning of January 2003.

Communicate is a reasonably modular construction. The application itself uses a number of plug-ins and bundles to provide functionality and sits on frameworks that provide the data and ensure it is available to the Web site. Essentially, Communicate is just a collection of user interface elements.

frameworks

Database Libraries

The OpenBase frameworks and MySQL C libraries are used in database plug-in modules that provide network access to the databases. The plug-ins are relatively simple and just read and write data over the wire to the database. Currently we have written just the two for accessing OpenBase and MySQL databases, but adding more database plug-ins is relatively straight forward.

FBTFPersistentObjects Framework

The FBTFPersistentObjects framework provides object persistence for business objects. It handles archiving and unarchiving objects to a data store, caching and uniquing. The persistence framework can be thought of an EOF lite, similar to Ruby on Rails' Active Record or other ORM libraries.

WSHDCoreData Framework

When starting the project, I decided to model Watershed's operations in a business logic framework rather than just build another Content Management System. By modelling the organisation's data and operations we get a framework that accurately describes Watershed but ,more importantly, is reusable. The WSHDCoreData framework can be used in any application Watershed needs to develop, such as a cinema audience tracking, exhibition planning… etc. WSHDCoreData contains the classes that describe Watershed: Events, Exhibits, Media, StaffPositions , Locations and the XML mapping file used by FBTFPersistentObjects to map to the persistence database. There is a companion framework WSWOCoreData, written in Java, that is used by server based web applications. WSHDCoreData has nothing to do with Apple's CoreData persistence technologies. It was named to describe it's purpose at Watershed before we were aware of , and Apple demonstrated CoreData.

Communicate

On top of the frameworks is the desktop application Communicate. Communicate provides access to the data through a series of list windows. Staff can add, edit and remove items from Watershed's programme.

interface screen shot interface screen shot interface screen shot

interface screen shot interface screen shot

Communicate will load plug-ins, from inside its own bundle, at launch time to provide control over the Web applications which generate the site data. In effect, these Communicate Site Plug-ins just save their changes into a shared preference database. An abstract plug-in class makes writing these plug-ins a quick and simple task. For most plug-ins, almost all the work can be accomplished in Interface Builder using Cocoa Bindings.

interface screen shot interface screen shot interface screen shot

Further Development

Communicate is being developed and extended and this will continue into the future. This includes new features to support new site and Web application features, UI and workflow enhancements and performance optimisations We learnt in the past that creating a new shiny site and just sitting back is guaranteed way to create lots of work in the future and a repetition of the process in an endless loop. We have made an internal commitment to constantly improve the watershed.co.uk sites, gradually over time, for an indefinite period. Some of the features we would like to add to Communicate in the near future are:

  • Comprehensive Applescript support.
  • A plug-in architecture for import, export and actions. Probably allowing plug-ins to be Cocoa bundles or AppleScript files.
  • XML Import & Export plug-ins.
  • A more user friendly and intuitive UI centred around a publishing paradigm and not requiring the user to have a knowledge of the underlying frameworks data structure.
|

Scriptable Applications

AppleScript is thought of as an 'added extra' for desktop applications but for internal tools it should be an essential on the 1.0 feature list.

Many shrink-wrapped, commercial applications do not add AppleScript support until later versions and with internal tools developed in-house it is an even lower priority. Many of these internal tools have a quick and dirty UI and none, or little, online help so being scriptable is not very likely.

With Cocoa applications adding AppleScript support is relatively easy and quick to implement. This is certainly the case in applications that are strongly based around a business logic model with data objects, as many internal tools are, and are already Key Value Coding compliant. Cocoa AppleScript support will do lots of the work for you.

So why should an in-house developer make their applications scriptable?

AppleScript support will free up developer time. If I write a scriptable application, the staff members who use the app or Oliver, Alex or Paddy of our ICT department can add new features or work around bugs without waiting for me to deploy the next version. I can spend my time implementing features that really need to be done rather than a feature to 'duplicate every cinema screening in Cinema One on every second Wednesday in the month'.

Watershed will gain from increased productivity from a scripted workflow. We could save so much time with a script that created the web data from the brochure:

  1. tell application "Quark XPress"
  2. repeat with every text box in the front document
  3. tell application "Communicate"
  4. set newExhibit to make new Exhibit
  5. set the title of newExhibit to the first line of text box
  6. save newExhibit
  7. end tell
  8. end repeat
  9. end tell

AppleScript support is a springboard or stepping stone to an Automator Action that even more users can make use of with even less training. Actions can be built by the users or by our ICT department.

If I ever left the Watershed, being able to implement some new features using AppleScript would give the users and ICT a breathing gap before a new developer could be employed and was up to speed with the application.

There are many other reasons to make any application scriptable that are often just applied to commercial development. These are just some reasons to add AppleScript support to internal tools.

|

Transparent Room Screen Saver

Been working on updating the Transparent Room screen saver for MacOS X recently. QuickTime movie of the screensaver (requires QuickTime 7). The screen saver uses Core Image, a Quartz Composition and OpenGL, making the minimum system requirement 10.4 with a graphics card.

|

odds&ends…
Benjamin Miller