James Dempsey and the Breakpoints

Illicit footage from WWDC on YouTube: James Dempsey and the Breakpoints singing Release Me, I Love View, and Designated Initializer.
It's probably the best-kept secret of WWDC: all the sessions are set to song. I hope someone posts Bertrand Serlet's crooning.
The Ongoing Debate Over Apple's Developer Documentation
Today I threw my assessment into the ring with all the others. See below:
The documentation debate is actually several orthogonal debates that regularly get intermingled:
* What should the purpose of the documentation be?
* How should it achieve those goals?
* Who should be responsible for it?
The answers to these questions used to be easy because of the scale of the problem: small. As Apple grows, as its software base grows, as its customer base grows, and as its developer community grows, so does the diversity of all of those and difficulties and vocal minorities with those difficulties appear.
An area where Apple has proved itself to be a master in its *products* is that of managing expectations. Only by managing expectations is it possible to consistently satisfy and delight customers. But we're not debating products here, we're debating *developer documentation* and that's not a product. Or maybe it is? I argue that it is not because if it were then Apple would be managing it like one and is clearly not. If it were, then the three questions above would have answers and we would all know and understand them. It is notable that for the iPhone SDK announcement there was a live public demo of Apple's developer tools -- a milestone event -- so maybe the path to the productization of the documentation is coming.
To add fuel to the multiple fires here:
Erik Buck wrote:
Many of those with difficulties know and understand this. It's the best thing since sliced bread and they want to learn, badly. They quickly find that the Cocoa way is not the same as the Java or C way, and while there are a small number who will simply complain that it is unfamiliar and do nothing about it, the majority just see it as another thing they need to grok. Programmers have one thing in common: they want to be productive. Those learning ObjC/Cocoa are doing it because they want to be productive and they believe that mastery will make them greatly productive."Cocoa is the most consistent, elegant, and productive software development technology I have ever used, and I have used a lot. Cocoa uses key metaphors and design patterns ubiquitously. If the programmer is either unaware of the metaphors or does not see their utility, it will be difficult to use Cocoa. If a programmer fails to grasp a particular pattern, the whole framework may be incomprehensible because the pattern is most likely used throughout."
But the documentation actually *defines* them very well. What the documentation does not do is to justify the existence of these things, explain the problems that they solve, put them into context, relate them to the user's situation, reassure the reader how much they need to know right now to get to the next step, etc. It does all of these things *in places* and to different degrees, but not consistently and not enough. The environment is hostile to learning."The attributes of Cocoa that make it so consistent and elegant are exactly the same attributes that I think newbies are complaining about. The newbie complains that the reference documentation mentions delegates or tags or data sources or the responder chain or key value coding or bindings or targets or actions etc. without defining them".
And this is precisely why there used to be sections in computer manuals on mouse usage, hierarchical file systems, etc., all unfamiliar concepts to many of the users. But there are *no such sections* in Apple's docs. Should there be?"This is exactly like a newbie complaining that clicking and dragging and selecting and double clicking are used throughout a GUI but not explained in the documentation for every application. Once the GUI metaphors are internalized, it is unnecessary at best and annoying at worst to keep encountering mouse based selection explained in every user's guide. The consistent application of the metaphors makes the GUI easy to use. The consistent use of metaphors makes Cocoa easy to program. BUT YOU MUST UNDERSTAND THE METAPHORS FIRST in both cases."
No really, I can't find them. Here I am in Xcode's docs and I'm typing in Concepts, Conceptual into the search box, and any way I try it I don't find any conceptual docs except Core Foundation Design Concepts (now three years old). And if I go to the developer site and find my way to http://developer.apple.com/documentation/Cocoa/, there is nothing labeled "conceptual". Let's try the pop-up on that page: Jump To: No concepts.
But the idea of conceptual documents *really is* there. The document Introduction to Cocoa Bindings Programming Topics has the following path: http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaBindings/CocoaBindings.html. It's in the *Conceptual* folder so I know that conceptual documents exist here as a concept at least. Now show me all the conceptual docs in one place so I can learn the concepts!
Now *I* know that the Guides fill a conceptual role, and Getting Started With Cocoa tells me to go see the Guides for conceptual information. But when I actually go there practically salivating with anticipation, it's organized by *Topic*, not by Concept. Show me the concepts!
Let's try a global text search at the developer site: 8600 documents show up, and I meet Core Foundation documents before I meet Cocoa. "Cocoa Concepts" does somewhat better with 1500 documents, but still. Where do I start? How about Core Data, that shows up near the front and sounds pretty conceptual....
Am I looking in the wrong place? For the wrong thing? How would I ever know if I'm starting out?
Perception is everything. If the newbie perceives that it's all screwed up then it *really is*. It doesn't matter if *you* learned Cocoa from reading the backs of cereal boxes. *You* are not the typical Cocoa learner. And this is key and why the docs need fixing: Almost nobody here is a typical Cocoa learner, because at least 90% of the typical Cocoa learners have not yet even arrived in Cocoaland.
On May 18, 2008, at 4:33 AM, Julius Guzy wrote:
Here is an example of the difficulty. Go to the Cocoa section of the Core Reference Library. Click on Fundamentals (Essential information for developers using Objective-C) and select Memory Management Programming Guide. I'm playing the part of a good newbie here -- clicking on everything that claims it is fundamental, essential, and core to the whole thing. By being here I am told that memory management is fundamental, essential, and core, and the document reinforces that. The first thing it says is:"The very good , interesting and informative debate in this list concerning the accessibility of the programming environment to new users has it seems to me incresingly polarised between those who think the documentation more or less adequate and those like me who for whatever reason, have great difficulties making use of the facilities."
"Memory management, especially as it concerns Objective-C programs, is an important subject. Some of the more common problems encountered by novice application developers derive from poor memory management. Cocoa provides mechanisms and a policy to assist you in the proper creation, retention, and disposal of objects."
Wonderful! I am in the right place. The docs are doing their job. But in the second section I find that:
"Cocoa does not use garbage collection. You must do your own memory management through reference counting."
Really? I'm sure that garbage collection was in here somewhere, that was one of the reasons I wanted to learn Cocoa all of a sudden. And then later on, there is a whole section on Core Foundation memory management, talk of retain cycles, NIB files, data sources, weak references, and the like. Whaaaaaaa?? If this is the fundamental stuff, what do the tricky bits look like?
The individual developer documents are of very high quality, and this particular document is no exception. It is well-structured, complete, consistent, has many references to other documents, stresses the importance of important things, shows simple examples, describes common errors, has good readable English, etc. Some of the developer docs I have read are truly exceptional, describing very difficult material very precisely and very clearly, so it's not a lack of writing skill or the ability to explain complex things.
So what is wrong?
1. This document that I believe to be fundamental, essential, core, and important *does not stand on its own* and so robs me of understanding.
2. It is at least five times too long and contains material that while fundamental, is not fundamental to *my learning as a newbie*.
3. It has not been designed as a learning tool. If it had then it would be goal-oriented and as a learner I would be able to measure achievement by reaching that goal. I would then have confidence that my task was complete and would tackle the next fundamental item.
4. It has not met my expectations
The last point is the most important one. For whatever reason I expected to be able to learn from the documentation and it was only when I tried to do that and failed were my expectations dashed. *As a product* the documentation fails for this one fundamental reason. If I were here because I had somehow missed the huge Learn Cocoa Here banner on the developer site and clicked in the wrong place, then it would be my fault (or I had continued past the banner that told me not to learn here). But there is no such banner and no such material, and so I have every reason to believe that I can learn here.
While the documents themselves are very good, I believe that the architectural problem is that the *documentation* is not. That is, as a body of work, it is on the verge of no longer meeting the needs of developers. That's because the nature of developers themselves has changed, and the current documentation organization does not scale. Apple's own product success (iPhone!) and ease of use sets a very high expectation bar for new developers too.
It is also worth noting that many (most?) younger developers expect (demand?) an agile approach to learning new languages, environments, and concepts. Maybe Cocoa is not like that and can never be like that, but this is the expectation that Apple faces. A Google search for "agile cocoa" yields no Agile books (but at least does lead to Fraser Speirs).
"I think this debate relates to the same concerns and battles we had, and in many cases are still having about computer usability, namely the effective design of human-computer interfaces, aka HCI.
It's a scaling problem. That fundamental memory management document above covers a lot of ground out of necessity (in order to be complete in the face of scaling) but in doing so covers too much ground (in the face of scaling of readership and hence the diversity of reader backgrounds).It is ironic that we are having this debate within an Apple programmer's mailing list. Apple has been celebrated for the usability of its machines and long may it continue to be so. However, Apple has been less celebrated for the humanity of its programming interface having, in my experience of Macs from the Lisa onwards, seemingly taken the attitude that its programmers were hobbyists, geeks essentially, who because of their enthusiasm would successfully negociate their way into the machine's innards. That said, the 9 tomes of "Inside the Macintosh" documentation of the programmer's workshop were pretty good once you got into them but there was a lot less to get into then than there is today"
"This question of volume of documentation and system size and complexity is significant to our debate. It is pertinent to quote what one of the great programming pioneers, Dijkstra said about PL/1: That it " must be like flying a plane with 7,000 buttons, switches, and handles to manipulate in the cockpit. I absolutely fail to see how we can keep our growing programs firmly within our intellectual grip when by its sheer baroqueness the programming language - our basic tool, mind you! - already escapes our intellectual control". ("Humble Programmer", see Prgramming Methodology 1978).
It's not the programming interface that has difficulty. It really is extremely good and very well thought out. Every time I do something the "right" way I end up with less code and understand a little more of how smart the people were who created all this in the first place.Well I think that the sheer size and complexity of Cocoa et al comes close to being an aeroplane cockpit and one which we are largely expected to learn to use from engineering manuals essentially concerned with listing the identity, composition and location of components,(not to mention the various incarnations of Xcode and Interface Builder). I am not saying that tremendous pains have not been taken to create a robust coherent system nor to document it. There have. Also there are many who have succeeded in mastering the system. Some will have done so through having grown up with it from early days, others will have had the fortune to attend (expensive) traning courses, and others still will have done so through dint of talent, perspicacity and hours of hard work. So mastery is possible. But I am not stupid nor a shirker nor a complete novice and I expect that the same can be said for most people who have had and are continuing to have horrendous dificulties in getting to use the system. It is clear that the programing interface to the Mac has serious usability issues."
It just doesn't look so good to those who don't understand why it is like it is. There are some similar arguments to this one about the interface to Blender, the 3D modeling and animation application. It's hell to start with (three button mouse? you must be kidding...), but much later you realize why it all makes sense because the enormous complexity you would otherwise face at that later point is so much more manageable than with other apps. Experienced Blender users are amazingly productive *and* they can keep up with the rapid scaling of the platform because of its conceptual underpinnings. The Blender docs are another matter though...
Exactly. Conceptually, saving triggers the connection and makes the magic happen. But how is that concept learned? Where is the metaphor? Why is saving even needed?"I cannot help touting one recent example. To specify the outlets etc between a class definition and Interface Builder on Leopard we are told to type the name of the class into a field and that thereafter IB will make the appropriate links with Xcode. It took me ten minutes of doing it this way and that before I realised that IB would only make the connections AFTER one had typed in the class name AND saved IB! We know from HCI research that it is little things like this which most frequently cause users to give up and never touch the equipment again."
Perception is everything!"Now, of course, as programmers we are well used to wasting hours hacking through the underbrush to discover that to switch on the machine we need to hold down Alt-Escape with the right hand whilst depressing a button round the back of the machine with the left. (That was how in the 70s one turned on some of the PCs at Leicester Poly!). But what a waste of time and effort and how demoralising and off-putting to anyone but the most obstinate programmer. I deferred having a go at Cocoa for about three or four years because I knew it would be a struggle and these last five months or is it six have been horrorshow.Let me state two principal facts.
1. Designing good user interfaces is hard.
2. Designing good user interfaces is expensive.
It is hard because every human being is an individual,
because there is seemingly a lot to learn
and because there are limits to the complexity we can handle.
It is expensive because the design of good HCI is primarily an empirical activity centered on user testing"
We have right here the results of some user testing and yet those results are not being accepted as valid. Why is that?
Let's look at the issues again, partition them the way I think they should be, and throw in some answers:"The question is then whether we and possibly apple should do anything about it"
* What should the purpose of the documentation be?
To hook beginners, to teach learners, and to support experts. These three different goals require mutually exclusive techniques and tools, but currently there appears to only be one big classroom and one textbook at Cocoa School.
* How should it achieve those goals?
Reward beginners with their concrete understanding; reward learners with their concrete code; reward experts with their concrete profit.
* Who should be responsible for it?
Ultimately it must be Apple because they control the code. They don't have to write the material themselves, but they must find a way make it possible for the material to exist and be found. There is no "Apple Press". There is little to no investment in a writing community that could fill in the gaps. There is no structure into which additional material can fit in a rational fashion, although there is all sorts of material out there that we stumble across.
Jason Stephenson wrote:
"I also don't find any great difficulty in using Apple's documentation. The conceptual documents cover the concepts, and the reference documentation serves as a reference. No, I don't think you should learn to use Cocoa just from the conceptual documents, but I'm sure it is possible.
Agreed. So what about third party books? Surely everyone is expected to go buy a book and use that. Well, not everyone does these days, since the web has taken over much of the world's publishing. But let's give the developer docs a chance and see what is recommended. Go to Getting Started With Cocoa at http://developer.apple.com/referencelibrary/GettingStarted/GS_Cocoa/ and look at Start Here. There surely cannot possibly be a better place to start! There are two book links:The simple fact of the matter is that documentation, just like a GUI, cannot be all things to all people. Programmers and those interested in programming are a particularly eclectic bunch. We each come at Cocoa for the first time with different experience, different reference points, and different expectations. One set of documentation cannot be expected to handle all of the possible permutations of programmer knowledge and experience. For this reason, other books exist written by third parties to cover those gaps or target different audiences."
http://www.oreilly.com/catalog/9780596003012/ leads me to a book published five years ago.
http://www.oreilly.com/catalog/9780596002350/ leads me to another book published five years ago.
And the other books on the page are of the same vintage.
You see the disconnect -- we all know that this is not how the great Cocoa books are found, yet here it is in the docs. Apple's documentation is not integrating well with other resources, and worse, maintenance appears to be falling behind somewhat. This is not a tenable position. Just as a city gets run down area by area, not noticed or lived in by those who could foster change, documentation, especially documentation on this scale fails in the same way with the new users in the slums.
Why this situation?
Jason Stephenson also wrote:
But since Apple is the de facto owner of Objective-C and the actual owner of Cocoa, there is no alternative!"In summary, I think it is a problem of all programming documentation and programming interface regardless of the platform or language, and I don't really see a way for a single vendor to resolve the issue, not do I think they really should"
The allocated resources at Apple are just not sufficient for the current task. It's not that the problems are not known or fretted about, it's a matter of priority. We and everyone else can debate and complain as much as we like, but no lasting changes will occur until this particular user interface is viewed on a par with the consumer user interface and dealt the resources commensurate with its importance.
Silicon Valley Cocoaheads April Meeting Now Available On Video

Two videos and slides of the April Cocoaheads meeting is now available online. For details, see Scott Stevenson's blog entry. There's a total of some 750MB of material.
It featured Scott (seen above) on user interface design, and Joar (below) digging into the debugger.

I'm in the audience asking intelligent questions.
Cocoa Programming for Mac OS X Third Edition
Cocoa: How To Sync Views With A Color Preference Slider
PP has a thumbnail browser, as any photography-oriented app has, and I want to be able to control its background color. Just like Aperture I have a slider in my prefs panel that changes the gray level of the background and need to have that stored in the user's preferences. Since PP is a document-based app (I can have many document windows open at the same time), changing the browser background has to affect all the open documents and should do so in real time so the user can see the result.
Here is my prefs panel. Not much there yet:

The slider is set up to deliver a number between 0.0 and 1.0 and continuously return its state. The prefs panel XIB file also has an NSUserDefaultsController. The file's owner is a custom window controller:

I use three separate mechanisms for keeping everything in sync: actions, bindings, and notifications. Here is all of it together in one diagram:

The slider, NSUserDefaultsController and preferences file are joined together with bindings. I bind the slider's value to NSUserDefaultsController:

so that the controller is notified of all changes to the slider value. The NSUserDefaultsController keeps the prefs file on disk in sync. That is 100% of what is needed to handle the slider and the prefs. To set up the initial preferences I create the defaults in my application object:

To keep the browser background color in sync with the slider I have to be able to tell the browser's view controller that the value has changed so that it can get it from the NSUserDefaultsController object. This is a two-stage process: the slider tells its window controller via its action and the window controller tells the browser view controller through a notification:

To update the browser views, each instance of the browser view controller listens for notifications that indicate the color has changed and sends the appropriate message to its view:

The above two methods are called at appropriate times by the browser's window controller. The color is updated like this as the notification is received:

Finally, to set up the initial color the browser view controller implements this code:
That's it. Having set all of this up I can use the same mechanisms for handling other preferences and parameters that affect the browser or other views. I'm understanding the value of following the MVC pattern: it gives everything a place -- if you can figure out where that should be.
How To Report A Bug

Home For Some: 1/1000s f/4.5 ISO200 200mm, -0.3ev, Canon 30D, Canon 70-200L IS f2.8
Apple has a long and comprehensive guide for reporting bugs. It strikes me that this information should be built in to the bug reporter page. If I have a Mail bug and I state that in the drop-down menu, then the Mail-specific information should appear so I know what to provide. Similarly the template format should appear as just that: a template.
But I know exactly what to do: file a bug. See rdar://problem/5815223. (That link won't work for anyone outside Apple).
Busy

The Road Ahead: 1/400s f/4.5 ISO200 200mm -0.3ev, Canon 30D, Canon 70-200L IS f2.8
I took the photo above of a cyclist as he went past at some speed, not knowing if I actually had a photo at all. There is a neat reflection of the road ahead in the sunglasses.
Not many updates recently since there have been many things competing for my time: family, taxes, work, job hunting, and coding being the main distractions. Some photography, but not a huge amount. Aperture is fast and there is just not a lot of news about it right now, so not so much to blog about it.
I've been working as a technical writer and advisor for a chip company in Cupertino for the last month, but that's a temporary position and the hunt for a real job continues. It's an interesting contrast to my previous employer: no phone calls, no meetings, and almost no email. I can actually sit and get things done all day.
After thinking about a photo-oriented Mac OS X application for a long time, I've taken the plunge and started bashing out code. I'm learning great chunks of Cocoa as I go along, so progress is slow but rewarding. I have no idea when I will be done, or even what "done" will be right now, but my plans are to make it useful and valuable enough that photographers will buy it. It will be Leopard-only.
Aperture Export SDK: Missing Files
It failed at this line:
#import <PluginManager/PROAPIAccessing.h>in ApertureExportPlugIn.h. The PROAPIAccessing.h file is missing because it and PROPlugInBundleRegistration.h are not included in either Leopard or the ApertureSDK 1.5.5.
To fix it , I copied across the Headers folder that contains those two from a Tiger back up. The full path is /Library/Frameworks/PluginManager.framework/Versions/B/Headers. There is already a soft link for Headers that is correct. It should look like this:

Aperture: Complexity Killed Aperture 1.5
I've looked a little at the internals of Aperture 2.0 and there are some striking differences between the two. It looks as if the whole core of the application has been rewritten.
Here is one thing I found. That diagram below is the data model for Aperture 1.5. The boxes represent information stored about things like versions, albums, vaults, adjustments etc., all the things that are carefully managed by Aperture and kept in a consistent state, all the things that are needed to display and manipulate images through the interface:

What matters is the number of lines in the diagram. I reckon there are about twenty. Each one is a relationship between the data in the boxes and many lines means much complexity and lots of work to keep things in order. And the impact of complexity grows very quickly as the number of items increases.
Now have a look at the data model for Aperture 2.0:

Still plenty of boxes, several more in fact, but hardly any lines. And only two are actual relationships, the two in the center representing inheritance instead. This means that Apple has eradicated the complexity inherent in the data model and moved it elsewhere, clearly to something faster.
This is at least partly why Aperture 2.0 is so much faster than Aperture 1.5. The other benefit of removing the complexity is that reliability should increase, so I'm hoping for a more stable, less buggy application.
For anyone wondering how I got hold of the data models for Aperture; it was easy. I just decompiled the .mom file in the application with Xcode.
Random Wok On Aperture 2.0
Note that Aperture 2.0 no longer displays a sheet to show progress. Instead, it exports in the background and progress is shown in Aperture's tasks window. Look at the bottom of the window next to the tools to see what is happening in the background:
Clicking on the spinner shows more detail:

A new feature in Aperture 2.0 is the ability to pause activity. That plus background operation makes exporting so much nicer.
[Update: A couple of problems have appeared if Random Wok 1.0 is used with Aperture 2.0. See below]
If used with Aperture 2.0 the behavior with respect to clashes with existing files has changed. With Aperture 1.5 Aperture would never supply an existing file name. However with 2.0 it does not check, and so plug-in exports will overwrite existing files with the same version name. Export to empty folders to work around.
If used with Aperture 2.0 the behavior with respect to clashes with existing random files has changed. Random Wok 1.0 will issue a warning if random files are exported that have a name clash, as before. However with 2.0, Aperture does not think that the export has finished and will show the export continuing forever in the background. This is usually only an issue if Freeze is used and repeated exports to the same place are performed. Quit Aperture and relaunch to fix.
C4 Conference Video Posted

Wolf Rentzsch has posted the first video from the C4 conference that took place in Chicago last year. It's entitled Indie Ethos, and talks about the place of independent Cocoa developers in the world.
Here is the complete list. One will be posted each week.
- Wolf Rentzsch: Indie Ethos
- Wil Shipley: Monster Marketing
- Daniel Jalkut: Application Acquisition
- Shawn Morel: Virtualization Vivisection
- Bob Ippolito: Exploring Erlang
- Adam Engst: Hacking The Press
- Tim Burks: RubyObjC & Nu
- Cabel Sasser: Coda Confidential
Update: Will Shipley's talk about hype is up now.
Waffle

Waffle is a blog that covers a multitude of geeky, webby, and Appley subjects. It's technical and meaty. But somehow never waffly.
[self setNeedsDisplay:YES]; Cocoa Programmer Tee-Shirt

Stand out in the crowd with a Cocoa tee-shirt. I've a few more ideas that I will convert into products when I get a chance.
You can buy this one from Zazzle. It has white lettering with the code [self setNeedsDisplay:YES]; on the front and the back. You can pick any dark color shirt you like. I get a cut of the proceeds.
Cocoa Cheerleaders

Cocoa Cheerleaders is brought to you by the team at PyrusMalus. It's a large collection of links to blogs, articles, books, code, and other information about Cocoa.
My interest piqued by the name, I discovered that PyrusMalus is the Latin name for the Domestic Apple tree. And there is a mystery application in the works looking for alpha testers called Atlantic.
The Mac Developer Network

The Mac Developer Network is hoping to become your first port of call for all things to do with Mac development. So far it features two Cocoa-oriented podcasts (iTunes links): Late Night Cocoa and Mac Developer Roundtable, both the creation of Steve Scott.
Late Night Cocoa is one-on-one discussion with Mac developers about many Cocoa-related subjects, while Mac Developer Roundtable is a group discussion.
Leopard: NSOperation and NSOperationQueue Example
Usually it is hard to use CPU resources efficiently and reliably, but these new features of Leopard make it simple. By packaging compute-intensive operations into NSOperation objects all the complexity is handed off to the operating system.
Notable in this example is that there is no code for locking. That's all handled automatically. There is also no code to manage dependencies between parts of the expression that are being evaluated on multiple threads and potentially multiple CPUs. Dependencies are expressed in the code, but not explicitly managed.
Since this is Objective-C 2.0 you can also see @properties and @synthesize at work. And marvel at the lack of memory management.
The site also has many other goodies for people using Macs for scientific environments including a script repository, and a Core Animation tutorial.
Leopard: New Developer Features

Deric Horn gave a very informative one-hour talk at Cocoaheads at Apple in Cupertino this week covering the developer improvements in Cocoa for Leopard. There are so many improvements under the hood that it is hard to remember all of them and Deric's talk served to fill in those blanks. Attendance was very good, and there were plenty of new faces. If you live anywhere nearby it is worth a visit. We meet in Town Hall, building 4. That's the auditorium where Steve Jobs introduced the new iMac, iWork, iLife, and .Mac in August.
I recorded the audio on my Canon S3 and edited it in GarageBand. You can download the file (AAC stereo, 30MB) via the Silicon Valley Cocoaheads page.
Learn Cocoa At Stanford

Stanford University has a Cocoa programming course. Information about the course, code, and the lecture slides are available online.
Leopard: Objective-C 2.0 Part Two
It's all good stuff -- both the features in Objective-C 2.0 and Scott's presentation of the material.
Leopard: The Ars Technica Review
Cocoa: Developer Changes In Leopard
Behind the scenes views must be his forte because his about page reveals every last detail about him, down to the color of his underwear (well almost -- but it does mention socks).
NSCoder Night

Chris Hanson has announced NSCoder Night: every Tuesday 7:00pm to 9:00pm. Anyone who is interested in Cocoa/Mac programming can show up at Orchard Valley Coffee in Campbell to get help, give help, show, write, debug, whatever. Scott Stevenson has an announcement that features a Google map.
The first meeting is of course after Leopard's release, so the NDAs will no longer apply and there will be much rejoicing.
The Knowledgeable Novices Syndrome
Why is it impossible to find a tutorial on how to write a jpg or bmp etc file from an nsview? surely this is basic bread and butter programming. I have finally got through the IB barrier, I can delegate, i can use a timer, i can draw paths with mice but i cant get to write a b****@@@@ view to file! I've been googling for 4 days now, i have "programming with quartz" in front ofg me as i write, i have "cocoa programming for os x" i have "vermont recipes", i have wall to wall bookmarks to the cocoa drawing guide and yet i still cant find a single example to get me on my way!!!!!!
And everyone is telling me how wonderfully simple it all is.
AAAARRRRRRGGGGGHHHHHH!!!!!!!!!!!!
I replied thus in the answer to this conundrum:It's not as if I havn't programmed most of my life!!
I write c, perl, pascal, prolog, cobaol, algol, fortran name ive programmed it. why can't i jkjlskadjlsajdljsdlsadflkahjsjdkalj
I have a name for this: it's the knowledgeable novices syndrome.
Ignorant novices feed fine by nibbling on the tasty morsels of code that Scott and other serve up. Ignorant experts need help with the unfamiliar utensils and spices, but are otherwise great code cooks. Knowledgeable experts make entire code meals table-side from raw reference materials in real time.
I'm one of these knowledgeable novices -- not as frustrated as the quoted poster -- but still encountering the same kind of issues.Knowledgeable novices are a challenge. They just want to make and eat a sandwich, but are having the darndest time doing it. Their knowledge is actually an encumberance, since they must unlearn what they think they know about sandwiches in order to make one the Cocoa way. There is no cutting of bread and spreading of peanut butter as they are sure there must be; just lamination, repitition, and bounding polygons applied to a couple of raw materials.
Cocoaheads Meeting October

Cocoaheads this month is Thursday 11th, and the subject is Google APIs for Cocoa. Scott Stevenson has a page with more information. I'll be there.
REAL Software Is Looking For A Cocoa Programmer

Geoff Perlman at REAL Software is looking for a Cocoa programmer to help them complete their Cocoa platform layer and improve their Mac support for REALbasic. If you haven't met REALbasic yet, it's worth a look. It's a cross-platform programming system for Mac, Linux, and Windows particularly suited for rapid deployment, similar in many ways to Visual Basic.
Cocoa In Pieces


Two new sites have appeared recently: CocoaTraces and CodeBeach that fit something of the same need. They are both repositories for pieces of working Cocoa source code, stored in such a way that it should be possible to find it if you need it.
Aperture Plugin: Integrating Localized Data Part 2

Now the strings in my code and my image are localized into French as well and English, I can move on to the strings in the interface. So far the French nib is just a copy of The English nib, created when I made the French nib localization. I used nibtool to extract the strings before translation, and I use nibtool again to put the translated strings back.
To do this I fire up Terminal, cd to the French.lproj folder and use the following command line:
nibtool -w new.nib -d file.strings Random_Wok.nib
This creates a new nib file with the English strings replaced by French ones. I use the Finder to replace the old nib with the new one and I am done. Now my resources look like this:

But if I run the plug-in in French, some of the strings no longer fit:

This is unfortunately typical for English. With its huge vocabulary, English can take up as little as 50% of the space of other languages. So my nice tight interface needs adjusting.
And there is a problem with subversion. After checking all of this in I find that my repository does not contain the French files and the NoImage.tiff file is in both the localized and the main folder:

To fix this I do two subversion things: svn delete the extra TIFF and svn add the folder and its contents. Svnx could do the delete, but not the add. After a fair amount of trying things that did not consistently work, I eventually went to the command line, did a svn add of the French.lproj folder, then quit and relaunched Xcode, then finally did a commit. That worked and now everything is synchronized.
I fix the layout with IB, but don't neaten it up yet. That's because I want to run it past my translator again to make sure nothing weird has happened that I won't spot. Once the translator has OKed it, I'll peek the pixels and straighten everything.
And then there is the "Images Selected" binding. I have this set up with two display patterns, one for the number of images and one for the pluralization string. For French I have to put the second string in twice since both words gain an "s" in the plural:

When I come to add German, this will break. The German strings are "Bild ausgewählt" and "Bilder ausgewählt". There is an "er" added in the plural, not an "s". Japanese is easy: no plurals exist in the language. A better solution is to put both singular and plural strings in the strings file and do all of this in code.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Integrating Localized Data Part 1

Now my translators have sent back localized versions of Localizable.strings, file.strings, and images that I sent to them, I can integrate them into my project. Here is how my project is organized right now:

To localize the NoImage.tiff image, I select NoImage.tiff and get Info, then click on Make File Localizable:

This changes the image into a group and shows the targets that it is associated with:

The Resources have been rearranged like this:

Clicking on the General tab shows the languages that the image is localized for:

I'm going to add French, so I click Add Localization and select French. The French image created by Xcode is just a copy of the English image at this stage:

To get my French image in, I change its name from PasDimage.tiff to NoImage.tiff so that the code will be able to access it with the same file name and replace the current image in the French.lprog folder via the Finder. Xcode has a handy contextual menu item called Reveal File In Finder to help with this.
To Localize the strings I do the same sequence, this time putting my English Localizable.strings into the English localization as well as the French Localizable.strings file into the French localization.
I localize the nib file too, creating the localization, but just leaving it as a duplicate of the English for now. I want to see how Random Wok works in French with what I have to far. Only a few things will be French at this stage: the progress message and the missing thumbnail image will show me that things are working correctly. But how to run in French?
I go to the International preference pane and move French to the top:

Now when I run Aperture it will be French.
But when I do, I find that the NoImage image is not there. And when I run it in English it is not there either. The problem is this code:

I get the TIFF file using a path that does not take into account the localization folders (English, French). So the initWithContentsOfFile method fails and returns nil. The fix is to use -pathForResource:ofType:inDirectory with a nil directory name:

And now the image shows up correctly when I run in French or English:

Next is fixing the interface strings.
The other parts of this series can be found via the Cocoa page.
A Guide To Objective-C for C++ Programmers
Aperture Plugin: Preparing For Localization

Random Wok 1.0 is currently not localized: the only language it supports is English. Before I release the 1.1 version I am going to add translations for the languages that Aperture supports: French, German, and Japanese. This requires extra files in the plugin's bundle that provide the language information.
To prepare, I replace all the messages and strings that are generated by the program and are human-readable with macros that retrieve the localized version. Code like this:

is replaced by code like this:

I use the NSLocalizedStringWithDefaultValue macro because it allows me to provide a key (exporting-images in this case) that is not the same as the string Exporting Images.... It also supports use of a specific bundle. I need that because Random Wok is a plugin and otherwise Aperture's main bundle would be used.
Once I have replaced all the strings in my code with macros, I use the terminal to run the genstrings utility on all the source files in my project:

I get this entry in the text file Localizable.strings created by genstrings for the exporting-images string above:
To provide for other languages the Localizable.strings file is duplicated and the string on the right replaced by the translation. When the plugin is run, the correct language files in the bundle are accessed and the key used in the code (exporting-images) matched with the entry in the file. Since the file is encoded UTF-16, the right hand string can contain ASCII and any unicode characters.
There are two other sources of strings that I need to worry about for localization in my plugin. First, the nib file contains all the strings used in the interface and it is currently English-only. To fix that I will need a new nib file for each language and will probably have to adjust the placement of some interface elements due to the size of the new strings. Second, I have an image that is displayed when there is no thumbnail available that says "No Image". That will have to be replaced with a new image for each language.
Apple provides a tool for helping with the translation of nibs called nibtool. Nibtool used with the -L option extracts all the strings from a nib file and sends them to stdout. The Random Wok nib file generates entries like these:

As before, a translation replaces the right hand string. Nibtool is used again to replace the strings in copies of the nib file with the translated versions.
The other parts of this series can be found via the Cocoa page.
CocoaHeads May 10: Write An Aperture Plug-In

CocoaHeads on May 10th at the Apple campus in Cupertino features John Anon giving a presentation on writing Aperture Plug-Ins. More information on Scott Stevenson's site.
Aperture Plugin: Problems With Arrays and Key Presses

A problem that I encountered along the way as I implemented my cache was that any change to the data always caused the array controller to load all the elements. This was exactly what I was trying to avoid with a cache, yet it was happening.
After much hair-pulling (and posting to Apple's Cocoa mailing list) I figured that the array controller believed that my array (implemented by methods in my Random_Wok class) was immutable, and therefore any observed change must mean that the entire array had changed and so need a reload. The fix was to make the array controller believe that the array was mutable. To do this I added three more methods:

These are the mutable array methods. I didn't even have to write any code for them because they are never called. They are just there so that the array controller knows that my array is mutable and so will allow updates to individual elements.
Another problem I found that was while the Page Up and Page Down keys worked on the NSTableView, the Home and End keys did not. A little odd. To fix this I subclassed NSTableView and overrode -keyDown:.
I created a custom class called BTKeydownTableView and told Interface Builder to use it instead of NSTableView. Here is the interface:

The implementation is very simple. I read the first character from the event queue and act on it:

To scroll the window to the right place I tell the view to scroll to the first or last rows, as required.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Caching Table Data

Retrieving the thumbnail image and generating the text for the table is time-consuming and called very often by the array controller that is controlling the table of images. This results in very slow scrolling of my table. To solve this problem, I added caching; the idea being that repeated requests for the same data come out of memory and do so quickly.
Adding caching was relatively simple, at least to implement simply. Here is the code:

The cache needs a key to cache on. For this I turn the index number into a string and use that as the key. The cache itself is a mutable dictionary that holds dictionaries:
This stores the data I get from Aperture fine and the interface is fast again. But now I have another problem. When the random file name parameters are changed, the cached data becomes stale. So each time a change occurs, I must update the cached text:

And the cached images:

This code enumerates through the cached data replacing the cached text and thumbnail objects. Bindings take care of the table updates. -updateCachedFilenames is called whenever anything changes that could affect the file name, such as a change of alpha case:

-updateCachedImages is called when the type of image changes:

This all works well, and is how Random Wok 1.0 was released. But it still has a problem. The cache grows forever, eating memory as it goes. And as the cache grows, the time taken to update the cached data grows. What is really needed is a cache that throws away the oldest entry once it reaches a certain limit and more data is added. So that is what I implemented next.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Improving Image Table Performance

My first attempt at providing table thumbnails was to load an NSMutableArray with all the images that the user has selected when the plugin starts. Unfortunately there can be a significant delay while they are retrieved from Aperture: above a few hundred the delay becomes irritating. Once the data was in my array, access was fast and the table scrolled up and down smoothly.
To improve start-up performance I replaced my array ivar imageTable with two methods: -countOfImageTableData and -objectInImageTableDataAtIndex:. Now, through the magic of KVC the array controller instantiated in the nib will access these two methods instead of the array. In fact it was trying to do that all along, failing, and falling back to accessing the ivar directly. The idea is that by generating the data on demand, the start-up performance problem will go away and the time taken distributed across all the images as they are viewed in the table.
Here is the code for the first method:

The check for the API version exists because my code simply does not support it. There used to be a less efficient way to retrieve thumbnails that I have no intention of coding. The result of this is that the image table is blank if an old API is detected. I also log a message to that effect on initialization, so it is not a complete mystery to the user.
The second method is very simple:

The code for -textForImageAtIndex: and -thumbnailForImageAtIndex: is not simple, however. But it is straight forward. The text code gets the properties of the image from Aperture and formats it appropriately. The thumbnail code is much easier to follow:

Master images (and potentially any image) don't have thumbnails, so I have to substitute. I do that with a small TIFF image that I put into the plugin bundle that says "No Image".
How did I do? Start-up was fast, even for thousands of images, so that problem was fixed. But there were other problems. Scrolling was dog slow. Logging showed that -countOfImageTableData and -objectInImageTableDataAtIndex: were being called for practically every pixel of scrolling I was doing, retrieving the image and generating the text each time. Horrible.
So the next step was to implement caching: retaining the images and text in memory and delivering those instead of getting them from Aperture each time they are needed.
The other parts of this series can be found via the Cocoa page.
HUDs Just Like Aperture's
Aperture Plugin: Implementing A Table Of Images

At this point in the project I decided two things: that I was going to release the plugin as 1.0 in a month, and that I needed to change the interface. I had already switched from a vertical arrangement with the prefix on top, to a horizontal arrangement with the prefix on the left. The trigger for this next change was the unsatisfactory display of the example random filename and the need to distinguish between what happens to the random naming when the freeze feature is used and is not.
The final version I decided to go with features a table that shows a thumbnail, plus the image version name, the image caption, and the new random name. This is the clearest way to show what is going to happen: show it happening.

By using the propertiesWithoutThumbnailForImageAtIndex: and thumbnailForImageAtIndex: methods I can get the data I need from Aperture for each table entry. So my plan was to bind the table to an array controller and bind the array controller to an array in the Random_Wok object that contained all the thumbnails and text. Updating the thumbnails and text would cause changes to propagate through the controller to the view as needed.
So I added an array controller to the nib and called it ImageTable:

I then bound it to the model like this:

and set up like this:

The model key is imageTableData and I use the keys thumb and text to access the data for the columns. So to support the needs of the array controller I implement an array called imageTableData as an ivar in Random_Wok and fill it with dictionaries with keys thumb and text, corresponding to NSImage and NSString objects I want to display.
I set up the table view like this:

Notice that I actually used a custom class for this. More on that later. The first (image) column is set up like this:

To display the image, I drag in an image cell. The properties of the image cell is accessed via the small triangle top right:

The inspector shows it as an NSImageCell, set up this way:

Now onto the bindings. The first column is bound to the array controller and uses the thumb key to get data from the model:

The second column is set up this way:

And bound to the text key through the array controller like this:

As I discovered while attempting to bind each table column to a separate array, setting up table column bindings also automatically sets up the the table bindings. This makes it impossible to bind separate columns to separate array objects: they have to all go to one object and then use the key path to get data from separate places. This pretty much means that you need an array of dictionaries to drive a table.
To provide data for the model, I loaded all the thumbnails and text into the array during the plugin initialization. Through the bindings I was able to change the dictionary contents and have the table update automatically. This all worked fine for a small selection of images.
However if I selected 500 images, the array would take a long time to fill. Worse, this was happening before anything was displayed (since the array controller was set to prepare content), making it look like the plugin has frozen. And changes to the text caused by changing the parameters for the random file names were also very slow because they too would be performed 500 times on all the items in the array.
So another approach was needed.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Better Logging

The logging I have been doing is very primitive. I call NSLog() to see what I want to see and have to manually insert and remove these calls (or mess with comments) to control what is going on. It's time for something better. Looking around, I found a handy logging class at Borkware that does much of what I want called MLog.
It implements a logging system that includes the line number and source file name with each message. This is very useful for understanding what is happening when reading the logs. The change I made to that code was to add control over the logging level.
Here are the definitions for my version of MLog.h. I implement seven levels and add the ability to revert to standard NSLog() calls, or to remove all the logging code completely:

Macros take care of inserting the correct code and extracting the file name and line number from the preprocessor. The class implements two class methods: one for actually logging messages, and one for setting the current log level. The idea of the level control is that the minimum log level can be set either by an environment variable or by code and only messages logged at that level or above will be shown.
The Implementation includes a static variable that holds the current log level:

Initialization is done in the class initializer:

It reads the environment variable MLogMinLevel and uses that to set the initial level. The logging code compares the logging level passed to the method with the current level and ignores those below the minimum:

The level setter code is very simple:

To use the logging, I add lines like this to my code:

and it provides messages that look like this that include the level, the file name and the line number:
Since I provide logging control with an environment variable, I can quite easily create build configurations that behave differently. If I go to the Project menu and select Edit Project Settings I can duplicate the current Debug and Release configurations and make two new ones:

Clicking on on the Build tab lets me set these up:

I edit the preprocessor macros to include the symbols I need to control the logging system. The Release No Logging configuration sets __BTREMOVE-LOGGING, for instance. Once set up I can change the current configuration I want to build and run from the main XCode window just by selecting it:

For this to fully work I edit the custom scripts I added to copy the executable and run Aperture, since the build names have changed and there are more of them.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Creating A Universal Binary

Random Wok is only being compiled for Intel right now. I need a universal binary so that it will run on the PowerPC architecture as well. This is easy to change. I select the Random Wok target:
Then click on the Info button, and under the Architectures tab, select what I need:

That's all there is to it. Everything happens behind the scenes.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Automating Builds And Using The Debugger

So far in this project all my debugging has been done with NSLog() calls since the code is pretty simple. To run my plugin each time I have been dragging the binary from the Build folder to Aperture's export plugins folder, launching Aperture, and then selecting Random Wok from the File > Export menu.
So how about debugging with the debugger? If I do a debug build, go through the same steps, and the run Aperture, my breakpoints are never hit. What is going on?
This is happening because the application, Aperture, is not being run by the debugger, and so my plugin is not being run by the debugger. To make XCode run Aperture I modified the instructions I found in a technical Q & A on Apple's developer site that shows how to handle this situation with a Web Kit plugin. In my case I create a new custom executable in the Projects folder on the left side of the XCode window and set it up this way:

Then I make sure that my build options for the debug build are set correctly: no optimization, generate all symbols, don't strip:

Now I can set breakpoints and have them hit:

I still have to copy the executable and run Aperture manually. But there is a way to fix that. I add a new run script :

And set it up like this:

The debug version is set up with a symbolic link and the release version with a copy. Here is the full text:
# clean up any previous products/symbolic links in the target folder
if [ -a "${USER_LIBRARY_DIR}/Application Support/Aperture/Plug-Ins/Export/${FULL_PRODUCT_NAME}" ]; then
rm -Rf "${USER_LIBRARY_DIR}/Application Support/Aperture/Plug-Ins/Export/${FULL_PRODUCT_NAME}"
fi
# Depending on the build configuration, either copy or link to the most recent product
if [ "${CONFIGURATION}" == "Debug" ]; then
# if we're debugging, add a symbolic link to the plug-in
ln -sf "${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}" \
"${USER_LIBRARY_DIR}/Application Support/Aperture/Plug-Ins/Export/${FULL_PRODUCT_NAME}"
elif [ "${CONFIGURATION}" == "Release" ]; then
# if we're compiling for release, just copy the plugin to the Internet Plug-ins folder
cp -Rfv "${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}" \
"${USER_LIBRARY_DIR}/Application Support/Aperture/Plug-Ins/Export/${FULL_PRODUCT_NAME}"
fi
Here is how the debug version looks in the Export folder:

Now when I compile and run or compile and debug, the script is run and Aperture is launched. Any breakpoints I have set work.
The other parts of this series can be found via the Cocoa page.
Subversion Part 4: Creating A Release

Now I have my repository set up, my code imported, and a working copy checked out that I am doing development on, I am going to release a snapshot of the current code as a beta, since I want some people to test it. In order to be able to keep working on the main code (in trunk) while I wait for feedback from testers I have to fork my code. If the testers find a problem and I fix it, I want to send out just the fix, not the current development version with the fix. Later I can merge the fix back into my main code.
Using subversion I do this by copying my code base in the trunk releases virtual folder into a new folder. After making sure that my project is checked in, I open svnX and make sure the correct revision is selected in the top pane. By selecting trunk:

and clicking on the svn copy button, I am given an opportunity to name and locate my new virtual folder:

I select branches as the location and fill in a name for the target of the copy. I add a message and hit commit:

And there is the new release in the wrong place. I meant to put it in releases. No matter. I just use the svn move button to move the beta1.0 folder to the right place:

Done. Now to work on the beta I check it out, or I can continue working on the trunk. If I check it out I have to create a set up a new build folder for it of course. Here is the current folder tree:

The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Displaying The Image Count With Bindings

To give some feedback to the user I want to include a display of the number of images that will be exported. I can do that by using bindings: by binding text on the window to an ivar in my Random_Wok object.
I create an ivar called imagesToProcess and an accessor to set it:

In -willBeActivated, I add some code to set it up before it is used:
And since changes in the image type (master or version) can change the number of images, I have to set it each time the export type changes:

That is all the code except for this method:

It returns "s" if imagesToProcess is more than one, otherwise an empty string. I need that in order to implement correct pluralization of my display string.
To display the image count on the window I add an NSTextField in the corner like this:

Its value is unimportant because I will be constructing it dynamically with bindings, but it helps to have a descriptive string there. I set up its bindings like this:

The Display Pattern string is what does the magic. Value1 and Value2 are bound to different key paths. The first to the value given by imagesToProcess, and the second to the plural string given by pluralImagesToProcess:

When these are substituted into the Display Pattern string, the result is what the user needs to see how many images are selected:

Because I implemented the accessors and I use them to change the value, bindings take care of doing this display updates automatically. If I select images in Aperture that include some with multiple versions of one master, the image count displayed changes when I click Master and then Version, just as it should.
The other parts of this series can be found via the Cocoa page.
Site Focus: The Hyperjeff Network
It's hard to describe The Hyperjeff Network succinctly. It's a Cocoa site with a huge amount of material. There are links to 17,436 native OS X applications, many with full source. There is an entire page of links to OS X-centrix sites. And don't miss the list of 361 links to articles about programming in Cocoa. Aperture Plugin: Making A Customized Button

The next feature I want to add is a Bagelturf badge that can be clicked to launch a browser to my home page. For that I need a graphic and some way of making a click open a URL. Since the badge won't obviously be a button, or have a button action, I want the cursor to change to a pointing hand to show that it is clickable.
After a lot of messing about with tracking rectangles I discovered that the correct way to implement the pointing hand was by using -resetCursorRects. I have to subclass NSButton and then override -resetCursorRects to define the cursor shape that I want when the cursor is inside my button.
First I drag a button in Interface Builder onto my window and select the Rounded bevel Button type. Then I create a new file and declare it as a subclass of NSButton using this code:

Then I add the implementation code:

To use the custom class in my nib file, I save those files and drag the .h file onto my nib file. This makes the nib file aware of the custom class and selects the class:

Now I can select my button and set its custom class to BTPointingHandButton:

By making a small image in Photoshop that has transparent background and saving it as a TIFF, I can have the image lay on the window background. To add the image to my XCode project I drag it onto the Resources folder and opt to copy it in.

Clicking on the image and using the inspector shows me what is there:

By dragging the image onto the button, the image is automatically set:

And finally I can add an action to File's owner via the attributes pane called bagelturfAction and hook it up to the button with control-drag. To make the action open a URL I add the action code to Random_Wok.m and use NSWorkspace's openURL method:

Done. Now if I hover over the button the cursor changes and a click launches Safari and goes to my home page.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Saving The Default Folder

As currently written the plugin defaults to the same folder each time I run it. This is not convenient. There is a very good chance that I will want to go back to the same folder each time, so I added the exported file path to the defaults that are written to a read from the prefs file.
As part of doing that I changed -defaultDirectory to this code:

Here I check to see if the value read from the defaults (_defaultExportPath) is a valid folder using NSFileManager. If it is, then I use it, otherwise I use the built-in default of ~/Documents. This deals with the user moving or renaming the folder between runs.
Another change I have made is to make the Generate button do something. I decided that using the current date and time would make for a good salt value:

And here is the latest interface:

Note that since I am posting this after the plugin was released, this interface design is no longer current.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Storing The Settings Between Runs

[Note: my blogging this project is some way behind the development. Random Wok 1.0 has already been released.]
An inconvenience with the current version is that it always presents blank text fields to the user. I would like to store the prefix, postfix, salt, use salt, random format, random length, and alpha case selection somewhere so that their settings are retained from one run to the next.
Implementing this turned out to be harder than I thought it would be. After reading up on NSUserDefaults and looking at several examples, it all looked straight forward enough. But something very odd happened: the values I were successfully saving and restoring between runs was being stored somewhere, but not in the com.bagelturf.Aperture.Export.Random_Wok file as I had been expecting. Not only were they not in the expected file, the expected file did not exist. It was never created.
The values I was storing were actually getting put into Aperture's own preferences file in ~/Library/Preferences. While under some circumstances this would have been the desired behavior (such as writing my own plugins for my own application), in mine it was not. I could not use most of the NSUserDefaults methods because of this side-effect.
So I created two methods: -getDefaults and -setDefaults and inside them used -persistentDomainForName: and -setPersistentDomain:forName to read and write the complete plist file as a dictionary.
Setting the defaults (that is writing the file) is done like this:

It first accesses the shared instance of user defaults and then uses the bundle identifier to retrieve the settings into a mutable dictionary. That dictionary is then updated with the latest values from the ivars, suitably encoded, and written back to the file. This allows older versions of the plugin to work with newer versions of the plist file: anything not used is simply left alone.
Reading the defaults is much more involved. The extra work comes from the need to set up the file if it does not initially exist and to manage plugin version changes. The first part is much like before, with the addition of reading the version from the plugin bundle:

Then I deal with the first run. This is indicated by an empty dictionary:

This results in a file on the disk, and a mutable variable defaultsDict with the same information. Now I am ready to deal with a change of version number:

I have nothing to do: there is only one version so far. If the version has changed, then the new version number is written to the dictionary and out to the file. Finally I am ready to set up the ivars from the dictionary values:

I call -getDefaults from the -initWithAPIManager method so the ivars are ready to go when the window is created. Then in -willBeActivated I set up the various elements of the view:

Finally in -willBeDeactivated I call -setDefaults to write the current state of the interface to the plist file.
Here is what the final com.bagelturf.Aperture.Export.Random_Wok.plist file looks like:

The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Dealing With Duplicate Random Names

Now that I am finally exporting with random file names I feel like I am on the home straight. But there is plenty more still to do: looking for problems, for instance.
There are two things that can go wrong with the export. First there could be an existing file in the folder I am exporting to. This is actually quite likely since repeated exports will create the same names for the same files unless the salt is changed. To solve that I add some code to display an alert if the rename fails. I modify the call to movePath by adding a handler. If there is an error with the rename, the handler method will be called:

To support the handler, I added two methods:

I had to incorporate a workaround for a bug: the return value of fileManager:shouldProceedAfterError is actually ignored by movePath:toPath:. NO always comes back. So I had to create an ivar to pass that value.
Although this code handles errors just fine, there is a better way of dealing with the situation of existing files with the same name as new files. I can create all the random file names and make sure that none of them exist in the folder before I even start the export. That will ensure that errors are truly exceptional (cause by hardware or other programs maybe).
Another problem I may run into is that there may be too many files for the randomness. Exporting 1000 files with three random decimal digits in the name is guaranteed to run into trouble.
So my code needs to ensure that all of the random names are unique with respect to themselves and to the destination folder. The solution is to create dictionary of existing filenames with NSNull objects. I use a dictionary for finding duplicates because they are very efficient for large numbers of images:

To that I add the files being renamed, the difference being that the files being renamed are stored with the new random name. The case-insensitive nature of the filing system means that I need another another dictionary to track file names coerced to lower case. Collisions in that dictionary mean that I have a problem.

My renaming code ignores the existing files by looking for the NSNulls and renames the rest:

The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Generating The Random Name and Renaming Images

Now I am able to generate the random string I can rename the images after they have been exported. So for each image file I do this:

To get the image UUID, depending on the API version I call either a method that gets the image properties with or without the thumbnail. I don't need the thumbnail, so I prefer to use the one that uses least memory. To determine which API to use, I add this code to the -initiWithAPIManager method:

To generate the random string I either use the salt string or not, depending on whether the checkbox is set. Then the random string is combined with the other parts of the name and used for the rename (achieved with the movePath:toPath:handler method). I add hyphens to the name in the code above for debug purposes. In real life the format string will drop the hyphens.
This code is not very memory-friendly. Each time around the loop will allocate more memory, so I need to refactor it with better memory use.
There. Finally making random file names!
The other parts of this series can be found via the Cocoa page.
Subversion Part 1: Downloading and Set Up

For a long time I have wanted to implement a version control system. The attraction is being able to gain control over my code without simply making duplicates of the project folders. In particular I want to be able to fork and merge different code bases and roll back to working versions. As soon as I involve anyone else in the development process (even testers), it is no longer just me and all this becomes a necessity.
So I am going to install subversion and move my active projects over to it. I have been reading up on subversion for a while on and off, and think that I understand it well enough to implement it and blog it. For the record I'm using XCode 2.4.1, subversion 1.4.3, Mac OS X 10.4.8. Since the interaction among these changes, bugs get fixed, and features get added, not all of this article will stand the test of time.
I'm going to concern myself with just a local repository. I don't need remote access yet, so that will do. This decision means that I don't have to deal with Apache, WebDAV, and a host of other issues that make the set up complex.
A significant cause of confusion is the number of folder hierarchies that have to be understood and managed in the setting up and running of subversion. So to make what I am doing crystal clear, I have some diagrams to illustrate and am using color to differentiate the parts.
I have to work with three different folder hierarchies, so I better get organized.
The first hierarchy is my current project, Random Wok. Its project folder is called Random Wok and its path is Documents/Progging/Cocoa/Aperture/. Inside Random Wok is the XCode project file, the Build folder, and all the source files and resources. Once I have put the project into my repository I will mothball the Random Wok folder. Currently I create new versions by duplicating the Random Wok folder and numbering them. These files and folders are yellow:

The second hierarchy is a new one. To use subversion I will put my code into a folder called trunk that lives inside a virtual folder hierarchy inside subversion repository. That repository (implemented inside a folder) will itself be one of a number, all organized into a real folder hierarchy on my hard drive. As per the subversion documentation, I will also have a branches folder and a releases folder at the same level as the trunk folder. So my first step is to create a folder Documents/Progging/Repositories on my hard drive. That is the real folder that will hold all my repositories. The first one will be called Random Wok Repository and will hold one project. These files and folders will be red.
The third hierarchy I have to concern myself with is that of the checked-out code. I will be checking my code out of the repository to this new location, building and debugging it, and finally checking it back in again when I am happy with the result. If I want I can then delete the code, because it's all in the repository. I create a folder called Working in Documents/Progging and another called Builds. Working is where I will check the source out to and will be green. Builds is where I will have all the Build folders live. I want to be able to keep the builds separate from the code. Inside Builds I create a folder called Random Wok. Inside that is where the builds and temporary files will be kept. The Build files and folders will be blue.
Now I need a copy of subversion. Subversion is actually a collection of utilities, svn and svnadmin being the two I will use the most. I download a prebuilt binary from Coding Monkeys and run the installer. That puts the binaries into /usr/local/bin:

To make using these simpler I set the PATH variable in the current tcsh shell invocation to /usr/local/bin with setenv by typing setenv PATH /usr/local/bin.
Next I need to run svnadmin to create my first repository:

And there it is:

Now I run svn once so it will create a folder called .subversion in my home directory:

Much more information follows that. In the .subversion folder is a config file. I edit this using TextWrangler with File > Open Hidden... and selecting Enable: All Files. In the [miscellany] section I modify the list of global-ignores so that it looks like this (it's actually all on one line):

The modifications make subversion ignore some extra XCode files that I don't care about. I don't include the *.pbxuser files as some people do. That is because those per-user values include the custom executable settings that I need to launch Aperture. Here is the hierarchy so far:

Next I need to set up the repository and import a project.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Generating A Random String From A UUID

The final part to implementing the randomness is actually making the real file names. To do that I write a method for generating a string from an NSData object using a character set and a length:

This first uses eight bytes to create a long long (64 bit) value and then uses repeated division to extract character via an index into the character set string.
Then I use that to help create the random string from the UUID and the salt:

First it makes a single string from the UUIDF and salt, then converts that to an NSData object and creates the MD5 digest of that. That is then used to generate the final string that will be used in the file name.
The other parts of this series can be found via the Cocoa page.
Late Night Cocoa

Late Night Cocoa is a podcast dedicated to Cocoa programming . It's very good. The hour-long podcasts are in the form of interviews with Cocoa programmers: XCode Projects, Scriptable Applications, Custom Views, Leopard, Core Data, and more. There is also an RSS feed.
Aperture Plugin: Creating The Example File Name

There are several parameters that depend on the settings for the random string format, the alpha case, and the length. I concentrate all the decisions for these into one big ugly switch statement that starts off like this:

This sets a character set for generating the random string, and also an example string that I will use on the display. Displaying the example random string now consists of this:

I truncate the example random part by the selected string length. Anywhere that the random string parameters change, I add a call to recalculate the parameters, such as in the action code for selecting the length:

Now the dialog looks like this:

The example file name now follows the settings.
The other parts of this series can be found via the Cocoa page.
Cocoa: I Will Be At Silicon Valley CocoaHeads Tonight

The second Thursday of each month sees a meeting of Silicon Valley CocoaHeads, usually on Apple's campus at 1 Infinite Loop. This month the topics are Core Animation and Related Leopard APIs (starter), and Cocoa Binding Tips And Tricks (main course). I will be there, trying to get my head around bindings one more time.
Aperture Plugin: Adding Randomness

To get my randomness I will use the MD5 message digest. That delivers a 128 bit number based on an input of any number of bytes. Since Mac OS X ships with MD5 functions in a dynamic library, all I have to do is to call that appropriately.
Appropriately in this case means using code that has already been written. Andreas Mayer submitted some code to the Cocoa mailing list that creates a category on NSData for MD5 and SHA1 digests. A Cocoa category is a collection of methods that extend those of an existing class. They cannot add ivars. The interface to the category AMDigest looks like this:

It declares two methods, each returning an NSData object with the digest inside. Since it is a category on NSData, the methods are used just as any other NSData methods are. The implementation code wraps some C function calls and returns a new autoreleased NSData object:

To add the methods, I create a new Objective C class called AMDigest in my XCode project and paste in the code. Left as is, this does not work, because I have not told XCode to link to the library and the function calls go unresolved. It took some work to figure out that the libcrypto.dylib file that is provided with OS X is a stub and should not be added to the project in the Frameworks and Libraries folder. All that is actually needed is to select the Random_Wok target and add this item to the build configuration:

To use the new method I also have to #import the header file that contains the interface in my Random_Wok.m file.
The plan is to concatenate the salt and the UUID of the Aperture image together and create an MD5 digest of that. I can then use 64 bits of the digest to create a random string based on the settings of the length, format, and alpha case that the user has selected.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Ending Editing

I discovered a problem with the Use Salt checkbox. If the checkbox is clicked then it disables the salt string text field. When it is clicked again, the field is re-enabled, but the string it contained is gone. Looking into this further I found that if I clicked away from the text field before disabling the field, the string would stick.
What is happening is that the field editor attached to the text field is not being told to end editing. Normally that is done when focus moves to another field or control, but not in the case of disabling the control. The cure was to change the -useSalt: method like this:

That call to -endEditingFor: tells the window manager to end editing for the field and all is well.
I also fixed the name "Random_Wok" in the menu and the window title. I edited the info.plist file by changing the display name string:

The other parts of this series can be found via the Cocoa page.
Cocoa: Resources For Programmers

Writing Random Wok and other code is always a very punctuated experience. I decide what to implement next, then read and take notes, then figure out what I think I need to do, then write the code, then debug, then blog. Sometimes a lot of time is spent during the reading and figuring out stages as I try to understand either how something is done, or, knowing how it is done, trying to understand how to do it that way in my code.
I go to several offline and online resources to get my curiosity satisfied, my brain overloaded, and my questions answered. Here is a short list of where I go, in order of attack:
1. XCode's Built-in Help and Documentation
My first stop is XCode, Apple's IDE for Cocoa development. XCode has a great deal of information built in: more than a gigabyte of Reference Library, in fact. Access to it all is via XCode's Help menu:

The documentation for XCode itself is found via the XCode Help item. The full developer reference library opens in a viewing window via Documentation:

The documentation window has two modes of searching. If you don't know how these work you will not find things and be very confused. Access the modes by clicking on the magnifying glass:

API search is immediate and needs no carriage return. The window below the search box shows the matches in order of fit. Click on one and I have the document in the lower pane. Double click and it opens in a separate window.
Full-Text Search is not immediate. I must press carriage return for anything to happen. And then the scope of the search is controlled by the selection in the library browser on the left:

My search for NSNull will only be carried out for documentation on Carbon in this case. I have to leave the Reference Library line at the top selected if I want to see all the documents (as I normally do).
The help menu also has two search items that work on selected text. Find selected text in documentation and Find selected text in API reference. An option double click on a word also takes me to Find selected text in API reference. I use this all the time.
Another very handy shortcut is to command double click on a word. That takes me to the definition in the code. This can be very useful because the context and comments in the code can often tell more than the documentation:

I set the languages that are searched via the Configure Options button on the toolbar:

I don't do C++ or Java and this prevents anything about them from showing up. Updates come out periodically. I have auto-checking enabled in the preferences:

So that I get update notifications:

And when a new set of documentation is available, 250 MB or so later, I have it.
Finally, I have access to the Man pages at the bottom of XCode's Help menu. I don't type "man" into a terminal: it is all there in the XCode documentation package.
2. Spotlight
The next place I go is Spotlight. I have all of the developer material indexed so spotlight will locate example code, saved web pages, and all kinds of other documents on my hard drive.
3. Cocoadev

Then I go to the web. Cocoadev is a WikiWikiWeb site containing a huge amount of user-contributed advice, code, explanations, links, and other material. It is easy to search, and you can contribute your own comments.
4. Cocoabuilder

Cocoabuilder is an archive of all of the postings to the Apple XCode, Cocoa, and Macosxdev mailing lists. There is a lot of Apple input on there. You can't post here. To post questions, sign up for the appropriate mailing lists at http://lists.apple.com/.
5. Borkware Quickies

Borkware and its associated Quickies page is a great place to find some of more useful and less documented ways of doing things. It's the creation of Mark Dalrymple, author of several Unix and Mac programming books and teacher at the Big Nerd Ranch.
6. Google

Finally, I Google what I am looking for. That will catch all kinds of things, including hits on the core foundation code, and open source efforts to duplicate parts of the Mac OS.
Aperture Plugin: Adding a Progress Bar

So far the progress bar supplied by Aperture as my images export does nothing. I'd like it to progress according to the fraction of images that have been exported out of the total number. Since progress for Random Wok includes a final renaming step that is very fast, I can display a progress bar that goes up to 90% for the actual export and then label the last 10% "Randomizing".
To implement the progress bar I have to set up the export progress structure and then update the current values that it contains as the situation progresses. The progress structure looks like this:

To protect it against simultaneous update by multiple threads an NSLock is used. That lock has to be claimed and relinquished by any thread that wants access, so protecting against partial changes caused by context switching. The SDK comes with two methods already included: -lockProgress and -unlockProgress that lock using an NSLock ivar called _progressLock.
I set up the progress bar just before I tell Aperture to start the export. The code I add to -exportManagerShouldBeginExport looks like this:

It does some math to make the progress bar go to about 90% of its full travel when all the images have been exported, then loads up the structure before starting the export. As the export proceeds, -exportmanagerShouldWriteImageData:toRelativePath:forImageAtIndex: will be called for each image. So it is that method that hosts the following code:

In -exportManagerDidFinishExport I have an opportunity to change the progress message and handle the randomizing rename. So I add this code to the beginning of that method to switch the message and use an indeterminate style of bar:

Notice that I do some memory management here with autorelease and retain. The example code does this, and I believe that I need to do it with the way I have handled the strings too. If not, using it in my code is harmless.
And here is the resulting progress bar:

The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Preventing Illegal Path Characters

Since the prefix and postscript strings will be going into a path, I also want to prevent the user from entering characters such as / and : that could cause unintended actions. There is no delegate method I can add to a text field to do this. Instead, a different class is used: a text formatter.
A text formatter can be attached to a text cell. Once attached, it can intervene in the formatting of the text to modify what the user sees. A phone number formatter would automatically add brackets and spaces as a number were entered, for instance. A formatter can also change the color and style of the text, and add and remove characters.
Since none of the supplied formatters do what I want, I will have to write my own. And to do that I have to create a new formatter class as a subclass of NSFormatter, the abstract base class that all formatters descend from.
The new formatter will be called BTValidPathElementFormatter and will prevent forward slash and colon characters from appearing, and optionally disallow leading periods and hyphens. Leading periods in file names make the file invisible in the Finder and that would almost certainly be unintended. Leading hyphens are not valid in a file name.
To create my new class in XCode I use File > New File and select Objective C class. Then I give it a name and make sure it is added to my project:

Then in the header file, I add the prototypes for the necessary methods, and add my own methods and ivars for handling the leading periods and hyphens:

The -isPartialStringValid: method allows me to figure out what was just added to the string, and then act accordingly to adjust the string and the current selection.
I need -init and -dealloc methods for my class. I use the -init method to initialize the superclass and up the default state of my ivars:

And I have to provide methods for changing the period and hyphen options:

These three methods are filled in with what is basically template code:

And finally here are the guts of the class: the code that disallows the characters. The first part sets up a character set that will be used to compare against the string in the text field. Then two easy cases are dealt with quickly:

The code uses NSRange structures to keep track of the current selection. Next, periods and hyphens and disallowed:

I'm not certain if the unichar cast is needed, but I did notice that the type returned by characterAtIndex: is of type unichar, so put it in to be on the safe side. Finally I look for the illegal characters in the text that was inserted. I can figure out what was added by comparing the previous and current selections:

And that is it for the new class. I have to make some changes in Random_Wok so that the new formatter is attached to the fields and set up correctly. I add this code to -willBeActivated:

It creates an instance of the formatter for each of the two fields that need formatting. Additionally the prefix formatter is set to reject hyphens and periods. Finally the formatters are attached to the NSCells associated with the NSTextFields using -setFormatter.
A delegate method to NSControl -control:didFailToValidatePartialString:errorDescription is called whenever the formatter returns NO. Since Random_Wok is already a delegate of the three text fields, this will be called. By coding it to beep, I can give the user audible feedback that the key press is not permitted:

And the code works. The prefix field will not accept leading periods or colons and both the prefix and postfix fields reject forward slashes and colons.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Continuous Example File Name Updates

There is a problem with the current interface: the user has to type in the prefix and postfix strings and then tab away from the field to end the editing. Until that happens, the example file name does not get updated. What I want is for the text fields to update the example file name continuously with each key press.
To do this I have to set up my Random_Wok object as the delegate to the text fields. If my Random_Wok class provides a -controlTextDidChange: method, then as a delegate, the NSTextField instances will call it for each change of the text.
I set up Random_Wok as the delegate in -willBeActivated: for each of the text fields:

And provide a way to distinguish between the fields by giving them tags:

I give them numbers 0, 1, and 2. Then I code the delegate to update the appropriate string and remake and display the example string on each call:

And that works. Typing, deleting, pasting, any change to the text fields is reflected in an immediate change in the example filename.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Using Bindings To Populate The Length Pop-Up

To populate the length pop-up I'm going to use bindings. Bindings allow changes in one object to automatically control another object. In this case I want the contents of the length pop-up to reflect the values I store in an array in my Random_Wok instance. Each time I change the array contents, the pop-up contents change. The advantage to this is that I don't have to write the code that populates the pop-up.
To make this work I need a third object: an array controller instance in my nib. An array controller is a pre-written, general-purpose controller that knows how to communicate with objects that expose bindings. It's like hiring a manager to run a department. Since it knows about arrays it can take a lot of work off my hands by dealing with the individual array elements for me. The array controller will mediate between changes in my data model and updates to the view.
To get an array controller instance into my nib I drag one from the controller palette to the nib file:

And I give it a name by double-clicking it: Length Popup Controller. To make the controller do anything useful I have to hook it up to a data model (an array) and a view (the pop-up). The controller then mediates between these for me.
The first thing I have to do is tell the controller what kind of object is in the array it is dealing with. "Good morning new manager, your staff consists of interns". In this case it is an array of NSMutableStrings that I will be modifying according to which string lengths I want the user to be able to choose:

Then I have to tell the array controller which array it will be dealing with. "Your interns are in that cube farm over there". To do this I bind the array controllers content array to the array of length strings in my Random_Wok object. That's an ivar called _lengthStrings:

I bind to File's Owner because that is a stand-in for my Random_Wok class. What this step does is to make the controller able to observe changes to the _lengthStrings array. The observation is incomplete at this stage -- the manager knows which cube farm to keep an eye on, but none of the interns yet know to tell the manager that anything has happened.
Next I have to tell the pop-up button that its contents will be supplied by an array controller. To do that I select the pop-up button and bind it to the array controller. The pop-up button has a binding called content that is designed for doing just this:

The controller key (arrangedObjects) is what the pop-up will observe to get its content. "Catering department, watch the manager's list of foods on his white board. Any time he changes it update the menu".
The last thing I must do is to add code to Random_Wok so that the object tells the NSArrayController that something has changed (remember that the interns in their cubes don't automatically tell the manager that their choice of foods has been updated). I code manual observer notification for this by using -willChangeValueForKey: and -didChangeValueForKey:

I need this manual method of notifying the controller that something has happened because my Random_Wok class is not Key Value Observing compliant. As a class it lacks the methods that the KVO mechanism requires to have change notifications automatically sent to observers. The array controller is KVO compliant, to the pop-up can automatically get notifications that the content array has changed.
Now the interns are saying to the manager "We will have a new list of foods in a minute -- OK, it's been updated". And the manager knows to update his whiteboard, the caterers see the new list of foods because they are watching the whiteboard, and the menu gets changed. And all along the interns don't know or care why they are creating food lists, and the caterers don't know or care where the food lists are coming from.
I try out the code and it works:

The final code for actually creating the array was more complicated than the little test I initially wrote:

This includes some extra logic to select the current length, or if not possible, use the highest length available. In this way as the user clicks around the available controls the pop-up does not keep resetting to the minimum length setting.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: A Better Way To Randomize

Reading the documentation for the Aperture export plugin SDK carefully I think I have found a better way of generating random names. What I really want is a random name per image, not per image name. If the name of the image is changed, the image should still get the same pseudo-random export name. If there is some sort of identifier that is tied to the image, not changing with the name, then I should use that.
And it does exist. By using -propertiesWithoutThumbnailForImageAtIndex: I can get a dictionary of properties for each image. Among the properties available is one keyed with kExportKeyUniqueID. This is a 22 character pseudo-random identifier stored in the library file for each image master and version. Here are some examples: Nku72ZxXT1CkGAF1JoQWRQ, mQ6wSngJQH29z0+o0yldKg, Jo1HuM/bRcu3u81dVYhdqw.
I looked at several thousand UUIDs by using SQLite Database Browser to export a table from one of my Aperture libraries. Then by analyzing the character frequencies I could see roughly how random the strings are and what characters are used. I found 0-9, A-Z, a-z, + and /. That is 64 in all, so I get 6 bits from each character, or 132 bits in all. That's four more than 128, which may explain why several of the characters have abnormally high frequencies (Q especially).
I can take the UUID, feed it and the salt into a hash function to get a 64 bit number, and then generate a string to satisfy the user's needs by repeated division of the resulting long long and conversion of the remainders into characters.
So the diagram now becomes:

The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Initialization Puzzle

A puzzle I faced when dealing with the interface was was how to handle initialization. What can be done when?
I found that -initWithAPIManager: is called once to instantiate the plug-in and that is matched with a single call to -dealloc, as expected. But that call occurs very early in the set-up and the nib file has not even been loaded. No nib file means no outlets, so although ivars can be set up, the interface cannot. So here I just set up default values for ivars.
-settingsView loads the nib and initializes it. Once that is done, the objects in the nib are instantiated. This method is called next. But it has no balancing method to undo anything I may have set up, so I add nothing to this method.
Next, -willBeActivated is called once and paired with -willBeDeactivated which is also called once. The documentation says that these are called each time the application is made active, but this appears not to be the case. Now that the outlets are ready, this is where I set up delegates and will initialize the state of the interface elements based on the ivar values. Anything I do that needs undoing will be undone in -willBeDeactivated.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Making The Interface Work

Now I need to add code to the Random_Wok class so that the interface does something. The alphaSet action is connected to the Lower/Upper/Both alpha case selection radio buttons. When the user changes the selection I get the selectedTag of the NSMatrix that holds the buttons and then update the example filename string:

The tags are set up to be 0, 1, and 2 for the three buttons via the Attributes pane of the Inspector:

And I have a typedefed enum called BTStringAlpha that encodes the values that the buttons can take.
The code for the length selection changing is a little different and took me a while to figure out. To get a number from the pop-up containing a list of number strings I need to get the integer value of the string that is the title of the selected item in the pop-up menu:

Changes to the postfix string just store the string and update the example string:

For that code to work, there must be an accessor _setPostfixString that stores the new string and releases any existing string:

If the Salt checkbox is selected I want to have the Generate button and the salt string available to the user. If not I want them grayed out. Here is the code that does that:

I use tags again for the four possible values of the string format pop-up. This time I have to update the state of the alpha buttons as well:

If Numeric is selected then the alpha buttons get grayed out, since they do not apply:

And for the purposes of debugging all of that, my example string display just shows the values that have been obtained from the interface:

This gives a display that looks like this:

Here the <1> comes from the selection of Hex, <14> is the length, and <2> shows Both.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Hooking Up The Interface

Hooking up the interface to an application is confusing for beginners. I've done it enough times to think that I know how it is done, but not enough times to be sure about it. So here goes.
For the Random Wok plugin, here is the nib file that contains the user interface:

The first area of confusion is File's Owner. What the heck is that? It's a stand-in for an instance of my Random_Wok object instance. It's called File's Owner because it is an instance of the Random_Wok class that "owns" the nib file (the "file" part).
The first thing I have to do is to tell Interface Builder that File's Owner is an instance of Random_Wok. I click on File's Owner, open the Inspector (shift command I), go to the Custom Class pane, and select Random_Wok from the long list of possibilities:

It would be so nice if Interface Builder would change the name of File's owner to Random_Wok after I had done this, but alas it does not. I have heard that many changes are coming to IB, so hopefully this is one of the improvements.
The next task is to tell IB what outlets and actions File's Owner (Random_Wok) has. More confusion because File's owner is an instance in the nib file and the outlets and actions are part of the class description. So with With File's owner selected, I click the Classes button:

and in the Inspector bring up the Attributes pane:

Those outlets represent pointers from the Random_Wok object instance to the interface elements in the nib file. That's how my plug-in will control the user interface. The SDK has already created these three for me, but has not connected them to the nib file (how would it know how?).
To make the outlet connections from File's owner to the views I control drag from File's Owner to the prefix NSTextField and click connect and then repeat for the postfix NSTextField, and to the Settings View NSView. This makes the prefix text field the first view for tabbing, and the postfix text field the last view for tabbing.

Actions are methods in the Random_Wok object that are sent messages when the user interacts with the interface. Clicking on the actions button shows that there are no actions for the File's Owner (Random_Wok) class yet:

To create the actions for the Random_Wok class I click on the Add button and fill in the names for my actions:

Then hook all of them up in turn by control-dragging from each of the controls to the File's owner instance and clicking Connect:

These actions coincide with the pars of the user interface that can send messages to my code: pop-up buttons, text fields, etc. Each action I create has to be added to the Random_Wok class files as a method and declared as in this example:

Having dealt with the actions I now need more outlets. The three that are provided for me are not enough: I have to be able to control many more aspects of the interface. So one at a time I add these to Random_Wok class, selecting the correct class for each one as I go:

Once that is done, I click back on the File's Owner instance, and use the Connections pane of the Inspector to connect up each interface element with File's owner, this time control clicking on File's Owner, dragging to the interface element, and clicking connect:

Finally I add each of these as outlet declarations to Random_Wok.h by adding this code:

Now when I compile and run I get this window instead of the generic controls:

I clearly need to adjust this a bit and clean up some problems, but now it is hooked up, I will be able to start writing the code that makes it come alive.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Designing The Interface

The user interface for the plugin is a single window. It's a combination of elements put there by Aperture and elements put there by the plugin. So far I have not defined an interface or hooked it up, so there has been nothing to see when the plugin is used except the Aperture elements.
In XCode the resources folder holds the user interface files:

And it is the nib file that describes the interface and contains instances of the objects it uses. Double-clicking on the nib file opens Interface Builder and shows the current Settings View:

I can remove that message, resize the window and add whatever I need to that view to implement the user controls. After some dragging and dropping, and more dragging, and more dragging, I came up with this:

It's not 100% there, but is good enough to get me to the next stage. The string format pop-up is populated with the four ways of creating the random string, but the length pop-up that follows is not. That will need to be filled dynamically by code depending on which choice of format is made.
It took me a little while to figure out how to add new items to a pop-up menu. Either drag a new one (called "Item") from the menu palette, or much more easily, just option drag an existing one.
Some of the interface elements will be disabled when they are not in use. For instance there is no letter case option if Numeric is chosen, so that will go gray. And if Salt is not selected then the Generate button and the text field will be gray and disabled.
The Example file name at the bottom will change dynamically as the user selects and types so they can see an example of the format. It will include the prefix and postfix strings in their proper positions and the random string in the middle.
I added the None strings in the text fields as placeholders in Interface Builder via the Attributes pane of the Inspector. They will appear whenever nothing is present in the field.

The next step is to join the user interface elements to the code and make them do something useful.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Saving The Image Files

Since my code just logs file names so far, the next thing I am going to do is change it so that it actually saves the data to disk. Initially I am going to save the files without using any randomization. Later I will implement the random naming.
The full path name to use comprises the name that I was given by -exportManagerWillbeginExportToPath: (and saved in _exportBasePath) with the file name given in the path variable in -exportManagerShouldWriteImageData:relativeToPath:forImageAtIndex:

And this works. I'm using the file names given to me by Aperture which means either the master file name or the version name depending on the user's selection on the dialog.
After some experimentation I discovered that Aperture always gives the plug-in a filename that can be written successfully. If a file already exists with the name of the image to be written, Aperture appends (1) or (2), etc. to the end of the name and gives that to the plug-in. So instead of trying to handle all the cases that Aperture is already handling for me regarding the naming, the strategy for generating random names should be to let Aperture do all the file writing with its own names and then rename the files myself when it is done. This actually makes randomizing the names easier because I will be in possession of all the information about all the files at the same time and won't have to maintain any state between naming image files.
To rename all the files at once I will have to make a copy of all the paths as I am given them. Once the exports are all done, I will generate a list of random names, and then use NSFilemanager -movePath:toPath:handler: to do the rename.
To store the names I create an NSMutableArray called _origFileNames and initialize it in the initWithAPIManager: method. Then in -exportManagerShouldWiteImageData: I add each file name to the array:
And in -exportManagerDidFinishExport I iterate through the stored filenames and do the rename:

For now I am just using a sequence of numbers 0001, 0002, etc. And I am being careful to keep the same file extension. My code is a little sloppy here with memory usage (what if this code is run with a huge number of files?) and I am not checking a lot of possible error conditions, such as existing files with the same numbered names. I will have to tackle problems like that when I write my final randomizing code.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: What Happens When I Run It??

So I compiled the plugin, copied it from the Random Wok/build/Release folder to the ~/Library/Application Support/Aperture/Plug-Ins/Export folder, and fired up Aperture (with a small test library!). The plug-in appears in the Export menu along with another plug-in:

But when I select some images and run the plug-in:

That's me. I'm the vendor for this plugin, so I better fix it. After some poking around and some help from another plugin author, it turned out that the info.plist contained these lines:
and should have contained these instead:
The difference is the underscore in the name. That's a bug in the template code: most but not all uses of the plug-in name have their spaces changed to underscores.
So I fix that (and tell Apple) and run again, this time getting this dialog:

The help button works, taking me to my help page. And I can select a version preset and whether versions or masters are exported. Canceling works. Clicking Export brings up a folder dialog and lets me select a destination. Clicking Export on that dialog makes Aperture process for a little while, and then it is done. The progress bar is not functional yet, and my only output is in the console log file:

So far so good. Next I need to make the code actually save the files.
The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Adding Basic Code

The template code in Random_Wok.m comes with some placeholders and empty methods that need to be modified slightly. By pressing control / (forward slash) I can skip to the next placeholder. The first code it finds looks like this:

I have to replace that with YES or NO depending on what behavior I want. In this case I want NO because I will not be supplying a list of presets:

I return YES for -allowsMasterExport and -allowsVersionExport because initially I don't care about the file format or contents, just the file name. For -wantsFileNamingControls I return NO. I'm going to set the name myself. For -wantsDestinationPathPrompt I return YES, since I want the user to supply that. And because the user supplies that, -destinationPath can return nil.
I do need to return a value for -defaultDirectory. I'm going to return the user's Documents folder by putting this code in:

This method is called when the user clicks the Export button. So I just tell the export manager to start the export:

When the export manager is ready to start the export it calls the -exportManagerWillBeginExportToPath: method and provides the folder path. That folder path will need the file name appended later. So I make a copy of that path in an ivar that I create:

Since I will be exporting all the images, this method always returns YES:

Confirmation comes back that the image will be exported. I don't need to know this, so I ignore it:

This method returns NO because I want to pretend that the plug-in wrote the image data somewhere. Instead I will just log what it did:

I have to tell the export manager that the export is done if the user cancels or if the export completes:

And that is it for now. What this should do is to give me a dialog that lets me select the export preset and whether I want the master or the version exported. Then I should get a dialog to select a destination folder that defaults to me Documents folder. When I click Export it will do nothing except log the image indexes and file names.
I compile it and there are no errors. A miracle! Here is the bundle in the Random Wok/build/Release folder:

The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Setting Up The info.plist File

Why write a plugin that assigns random file names? I can think of several reasons:
• It has not been done yet (always a good reason)
• There is no random file naming feature in Aperture, so it is creating new functionality
• Random file names for images posted to the web can be changed periodically. This prevents people from deep linking to them and using your bandwidth
• Random file names for images that are subsequently sorted in name order will have a random order. This is useful for creating a random image order for imports into other applications
• It removes all meaning from the file names and so makes them neutral
• The order of sequences can be hidden
On with the task. An Aperture plugin is a bundle: a folder that looks to the user like a single file. Inside that bundle goes the executable code, plus all the resources: interface elements, menus, images, strings, etc. A necessary part of the bundle is the info.plist file that contains basic information about the bundle itself.
The info.plist file in my project has already been partially set up by the XCode template. I have to edit some of what is provided: the CFBundleIdentifier string for instance.
There is a help URL that I can set that I will need to ensure is active on my site:

And there is a location that requires a UUID. That's a globally unique identifier. So I run that utility and it gives me a string:

That is it for the plist. I don't believe I have to do anything to the rest of the bundle at this stage, but next I will have to write some code to get some basic functionality out of my plugin.
The other parts of this series can be found via the Cocoa page.
Aperture: Writing An Export Plug-in
Since I have XCode 2.4.1 already installed, the first thing I have to do is to get the Software Developer Kit for Aperture 1.5.1. That is downloaded from Apple's developer web site. Once logged in it is found under Applications in the Downloads area. Since I'm an ADC member (a free, cheap Online member) I have a log-in and can access the disk image that I need. Sign up to be a member if you are not already. Doing so will subject you to a NDA, so beware. I certainly won't be giving any secrets away in writing about my attempts. Note that the documentation for the Aperture Export SDK is publicly available.
Once I have the SDK disk image I open it and install it and copy the documentation to a folder on my hard drive. The Read Me contains instructions on how to integrate the documentation into the XCode documentation viewer. It is worth following those instructions because it makes accessing it very easy.
The SDK installs a template that can be used as a starting point for writing a plugin. Creating a new project in XCode allows me to select the plug-in template:

I give it the name Random Wok and my project is created:

Random_wok.m is where most of my code will live.
I have limited tools to work with. The template contains some starter code, and the installation of the SDK installed an example plugin called SampleFTPExportPlugin. And there is the documentation, and that is it.
My understanding of the way this plugin works and what I need to do is also limited. I know that I have to set up some data in the info.plist file to describe my plugin, create and edit any interface elements I need with Interface Builder, and manage the plugin interface. As far as I can tell, the way the plugin works is that my interface is presented to the user and then everything is event-driven based on what the user clicks and what Aperture gives me. The plugin can either have Aperture write images to files or can handle the image data itself. I have to at least partially understand the dance that is required between Aperture and the plugin.
I also have to set up a number of methods so that Aperture knows what to do with my plugin. For instance -(BOOL)allowsMasterExport returns either YES or NO depending on what my plugin is capable of. The first thing to do is to fill in enough to make it compile and do roughly what I want and sprinkle some NSLog() function calls around so I can see what is happening.
The other parts of this series can be found via the Cocoa page.
A Taste Of Cocoa

Scott Stevenson has gone to great lengths to post an introductory article on learning Cocoa. He describes it thus:
Send Scott money if you like what you see and he will spend more time on this endeavor. The message of this tutorial is that Cocoa is a very powerful tool. I'm interested to see what is next.
Building a Game Engine with Cocoa

Matthew Russell has written a good introductory article to Cocoa for O'Reilly's Mac Dev Center that shows how to build a simple game board with pieces that can be moved with mouse clicks. It's good because it shows a simple and comprehensible graphics application without unnecessary complexity and with clear explanation. There is a link to the project code at the end of the article. Other Cocoa articles at O'Reilly can be found on their Cocoa Programming page.
Aperture 1.5: Export SDK Now Available
What is that? It's a set of files used by programmers who want to add functionality to Aperture's exporting capabilities. Programmers use the SDK with other Apple tools to create plug ins. Once installed, they show up in the File > Export menu. The idea is that anyone who wants to export from Aperture to another application or web service can now write the necessary code to do so.
The code is currently pre-release, so it is only available to Apple developers (who are subject to non-disclosure agreements). Go to the developer site and log in to download it. However the reference documentation and the SDK overview are freely available.
Key Value Coding 10: Do Some Math

There are the five ivar text fields that were there previously, but now five more are added that do math on the other fields. Doing this used no code, just key paths. AppController implements the functionality like this:

To calculate the sum a key path with a special @sum element is used. So in the example above, the key path is collection.@sum.passengers. That means to get the array referenced by collection and apply the passengers message to each element, then sum the returned values. So this gets the sum of passengers for all the vehicles in the collection.
The other fields work the same way. The only odd one is the count of values. That one uses the key path collection.@count and does not need an ivar name to do its work since it is just counting array elements.
So far I have shown KVO doing the work of accessors, storage management, and math. Saving code, errors, and maintenance. Key paths do more than that, but a visit will be postponed until some other things have been covered.
Key Value Coding 9: Getting An Array

The VehicleCollection class does away with the two NSMutableArrays and replaces them with one:

and Vehicle now includes the type as an ivar:

These changes mean that AppController can now access an entire array of ivars with a single key path:

Look carefully at what happened there. The key path collection.paint when accessing mytoys returns an array of paint strings, one per Vehicle in the VehicleCollection object. So now KVC is saving us iteration over arrays by doing all the message passing and result collection for us. This works because collection is an NSMutableArray. That's very different from the way that NSMutableDictionaries work on this situation.
Key Value Coding 8: Accessing An Array

In KVO8 the VehicleCollection class has the NSMutableDictionaries replaced with NSMutableArrays:

The code to access the ivars in AppController is different now since the array is indexed:

The first valueForKey is used to access the NSMutableArray ivar by name, either cars or bikes. Then objectAtIndex is used to access the Vehicle in the array, and finally valueForKeyPath accesses the ivar by name. So that is three accesses to get the data.
Key Value Coding 7: Accessing a Dictionary

When you enter the ivar name at the top it displays information in one pair of fields or the other depending on whether the vehicle is a car or a bike.
All the changes in this code are in the AppController. It accesses the ivars like this:

Look at the variable d. It is set with a key path that looks like cars.limo. So that returns a dictionary and the rest of the code accesses that dictionary with valueForkey. nil values are returned if the vehicle named is not in the dictionary, so the code uses that to fill in the correct fields of the window.
Key Value Coding 6: A Longer Key Path

Most of the changes are under the hood. Instead of using an NSDictionary like KVO5 did, KVO6 uses a custom container class called VehicleCollection. VehicleCollection looks like this:

It has two ivars cars and bikes, each storing Vehicles. This adds an extra level of selection to the key path. Notice that the VehicleCollection has no accessors. The -init code looks like this:

Notice that to set up the vehicles it uses setValue:forKey: on the Vehicle class. That's because the Vehicle class doesn't have any accessors to use. This brings up another useful aspect of using KVC: KVC does the release/retain for you. When setValue is used, it takes care of the reference counting automatically, releasing the old object and retaining the new one. Less code to write, debug, and maintain.
The application controller uses an ivar called mytoys to store a pointer to a VehicleCollection:

Now comes the interesting part, accessing the ivars of the Vehicles inside the VehicleCollection. AppController does it this way:

The key path now has three parts. If the user types "paint", then the first ivar access is to cars.suv.paint. This accesses the cars ivar of the mytoys vehicle collection, then the NSDictionary of the VehicleCollection to find the suv object, then the paint ivar of the Vehicle object. All with no accessors. There is no limit to the length of key paths.
Key Value Coding 5: Adding valueForKeyPath

And it does the same thing. The only difference is in the way that AppController accesses the ivars. Here is the new code:

And here is the code from KVO4:

KVO5 uses valueForKeyPath once instead of valueForKey twice. If the user types in "paint", it takes "suv" and "paint" and concatenates them with a period to get "suv.paint" and passes that to keyValueForPath. That path is parsed with the first part being applied to the NSDictionary to get the Vehicle and the second part being passed to the Vehicle to get the ivar. It's all done behind the scenes.
Key Value Coding 4: Using A Dictionary Instead Of ivars

This Key Value Coding example replaces the individual ivars of KVO3 with a dictionary and adds a third car. It also replaces the Car class with a Vehicle class. AppController now contains an ivar called cars that is an NSMutableDictionary containing Vehicles:

The Vehicle class has no accessors.
AppController reads the values out by using valueForKey twice: once to access the dictionary to find the Vehicle, and then again to access the ivar inside the Vehicle object:

And since the Vehicle class handles the unknown key case by returning an "Unknown: " string:

we get the same kind of result as we did for the Car class.
Key Value Coding 3: Adding A Custom Class

You enter the name of the ivar you want to see and its values are shown below when you press Show. The Car class looks like this:

and its implementation is:

It simply sets up default values for each object instance when initialized and handles thee undefined key as before. There are no accessors here. If I try an ivar that does not exist, I get the Unknown message again:

In AppController, I have two Cars defined, suv and convertible.:

and the code that accesses these is:

The message valueForKey is simply sent to each of the Car objects to get the values for the fields. And I didn't write any accessor code for the Car class.
Key Value Coding 2: valueForUndefinedKey
KVO1 had the problem that if you type in something that does not correspond to an ivar name, such as george, you get this:

The bottom field was never updated. And that is because this happened:
What this is saying is that it failed to find an ivar called george, and so was unable to continue. It looked for accessors first, then gave up and tried to access the ivar directly and that failed too.
There is one change in KVO2. It adds a new method, one that is part of the Key Value Coding informal protocol:

If the key used (entered in the top field) cannot be found, then instead of giving up, this method is called. It simply prefixes the key with "Unknown: " and returns it as the ivar value. lets see it in action:

And now there is nothing in the run log.
Cocoa Programming: Key Value Coding
The articles in this series each reference an XCode project and are targeted at XCode 2.3 and Mac OS 10.4 (Tiger). The projects are available on the download page and are numbered KVO1 on up. Yes, the KVC articles use projects called KVO because eventually it will progress to KVO.
KVC1: See KVC In Action
The code for this article is called KVO1, available on the download page. This project consists of a single class, AppController that looks like this:

You can see the three ivars I am interested in accessing: bob, jim, and fred. The idea here is that I will access these without writing any accessors. So the application lets the user specify the ivar by name to display:

The user types in the ivar name in the top field, presses the Show button, and the bottom field shows the value. That's all it does. Build the project and run it.
Here is the whole of the code for AppController:

-awakeFromNIB: does the initialization. It does access the ivars directly, and that is OK because the I don't care how the values are initialized. This program is written to show how KVC is used to read values.
All of the interesting stuff is in -show:. name is read from the top field and then used as an argument to valueForKey: to retrieve the string from the appropriate ivar and write it to the bottom field.
The run log shows the string Hello being output by the NSLog line:
If you type in something that does not correspond to an ivar name, such as george, you get this:

The bottom field was never updated. And that is because this happened:
What this is saying is that it failed to find an ivar called george, and so was unable to continue. It looked for accessors first, then gave up and tried to access the ivar directly and that failed too. I will improve on this behavior in the next installment.

