Condensing The Rain
June 10 2011 Tweet
In the newly-released CloudApp for Mac 1.5, a lot of features have been added and revamped. While creating this new version, we had to account for Mac App Store’s guidelines, especially for loading external code. The raindrop engine got completely rewritten from scratch, and the result is quite fantastic (at least we think so).
The Early Days
The first versions of CloudApp, 1.0 through 1.0.3, used the “typical” Cocoa way of loading code: NSBundle. It worked for what we wanted it to do, and that was load code and be able to easily pass large amounts of data around. In addition, we had it run in a separate thread, which kept the app nice and snappy while waiting for, say, Photoshop to export its canvas data.
Cloud.app would find available raindrop bundles, load them into memory, and keep a reference to the target bundle ID (the application that that raindrop interacted with). When the global hotkey was triggered, we would find the frontmost application, see if there was an applicable raindrop, and run it if there was.
From a first-party standpoint, that was great. However, we soon looked at the possibility of opening this to third party developers. Some problems became evident quite quickly.
Houston, We Have a Problem
Our initial worry was trusting third-party code. It’s a scary thought that someone else’s code would be getting directly loaded into the same memory space as your own. If another developer didn’t handle an exception during the raindrop sequence, the entire Cloud.app would crash. This is a problem for the integrity of CloudApp, and needed to stay stable even when raindrops contain crashers.
Another worry was class conflicts. Loading two classes with the same name, one from CloudApp and the other from a bundle, is bound to cause issues. The Objective-C runtime doesn’t like having this conflict, and throws out one. As a result, there can be unexpected behavior in either CloudApp or the raindrop bundle. This would unreliability was not okay for us, so we looked for a new solution.
Out With the Old, In With the New
Our new raindrop engine is similar to how WebKit handles plugins. Every raindrop is actually its own separate process, and runs independently of CloudApp. If a raindrop were to crash, it would crash the external helper process and not CloudApp itself. Therefore, worry one was alleviated.
Also, since it has its own memory space, there would be no class conflicts with CloudApp. Behavior would be as expected, and all would be well and good.
The Technical Details
How does this work, exactly? The external process is a binary executable that sets up the raindrop, getting it ready for action. It communicates over Distributed Objects to the main CloudApp. CloudApp keeps references to the remote raindrop abstraction layer. It asks the remote raindrop abstraction layer to get the relevant data and call back to CloudApp.
The data is passed between processes by using a unique NSPasteboard. The raindrop passes back the name of the pasteboard. CloudApp reads the data, then destroys the pasteboard. What’s great about this approach is that we have a single handler for all data inputs for uploading. Both dropping on the menu bar item and raindrops result in NSPasteboardItems that we parse and deal with properly.
Honey, Bring Your Umbrella
Another change that we made with the new version is a new type of raindrop: downpour. This type of raindrop stays open constantly and can request a new upload at any time. It allowed us to move our auto-uploading of screenshots into a raindrop, further making CloudApp like a data hub. We have some cool ideas in mind of how developers can utilize downpour raindrops, and can’t wait to see what people come up with.
Lions, Tigers, and App Stores… Oh My!
When mixing the Mac App Store and external processes, much care has to be taken. First of all, processes cannot stay open after the main application has quit without user consent. The raindrop processes much watch the parent process and terminate when it does.
We took this a step farther, though. Raindrops that are only applicable to a certain target application, such as Photoshop or Finder, are only running when they are needed to be. When Photoshop launches, its raindrop process launches. When it quits, so does its raindrop process. CloudApp and raindrops only use resources when they are directly relevant to the user.
With all of these new ways to upload, make sure you upgrade to CloudApp Pro so you never have to stop sharing.