Cocoa

James Dempsey and the Breakpoints

dempsey
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

There's a huge debate going on right now on Apple's Cocoa list (archives on Cocoabuilder) about the suitability of the current developer documentation for Cocoa and Objective-C.

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:

"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."

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.

"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".

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.

"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."

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?

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:

"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."

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:

"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 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"

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).

"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).

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'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.

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...

"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."

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?

"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"

Perception is everything! 

We have right here the results of some user testing and yet those results are not being accepted as valid. Why is that?

"The question is then whether we and possibly apple should do anything about it"

Let's look at the issues again, partition them the way I think they should be, and throw in some answers:

* 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.

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."

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:

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:

"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"

But since Apple is the de facto owner of Objective-C and the actual owner of Cocoa, there is no alternative! 

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

000570-meetingintro-t
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.
000570-xcodedebuggingintro-t
I'm in the audience asking intelligent questions.
|

Cocoa Programming for Mac OS X Third Edition

The third edition of Aaron Hillegass's book Cocoa Programming for Mac OS X is now available. It's been brought up to date with garbage collection, XCode 3.0, Core Data, Core Animation, and Leopard.
|

Cocoa: How To Sync Views With A Color Preference Slider

Much of my spare time is being used up very gradually writing a new photography-oriented application which I have code-named PP. This being my first sizable Mac app I also spend a lot of time reading, figuring out, rewriting code, and debugging as I encounter each challenge and work through it. While I am not going to blog the entire thing as I did with Random Wok, I am going to contribute pieces of what I have learned along the way.

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:
prefssync20
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:

prefssync21
I use three separate mechanisms for keeping everything in sync: actions, bindings, and notifications. Here is all of it together in one diagram:
ppprefssync
The slider, NSUserDefaultsController and preferences file are joined together with bindings. I bind the slider's value to NSUserDefaultsController:
prefssync6
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:
prefssync3
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:
prefssync4
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:
prefssync2
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:
prefssync
Finally, to set up the initial color the browser view controller implements this code:
prefssync5
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

IMG_6827-2008-03-09
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

IMG_6828-2008-03-09
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

For anyone who is trying to make an export plug-in for Aperture: make sure you keep a copy of your Tiger development environment backed up somewhere. I installed Leopard and the dev tools and found that my Random Wok plug-in would not compile.

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:
aperturesdk
|

Aperture: Complexity Killed Aperture 1.5

James Duncan Davidson has posted an interesting article about his meeting with Apple's Aperture team. He told them what irked him about Aperture as a photographer and they went off and fixed it. But how did they fix it?

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:
mom15
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:
mom20
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

My Aperture export plug-in, Random Wok, runs just fine [Update: not quite; see below] with Aperture 2.0 and Leopard. The version I'm using internally has some additional features and I have recompiled it using XCode 3.0, idea being to release an update as soon as I can muster the time. My big hang-up was trying to localize the plug-in. This is a tremendous amount of work and requires lining up volunteers to do the translation and much on the management side of things. So going forward I have decided to not support localization.

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:
export1
Clicking on the spinner shows more detail:
export2
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

c4
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
If you are interested in what is happening at ground level in the Cocoa developer world, watch these.

Update: Will Shipley's talk about hype is up now.
|

Waffle

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

selfsetneedsdisplayyes
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

cocoacheerleaders
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

macdevelopernetwork
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

Drew McCormack at MacResearch has a nice example of the power of NSOperation and NSOperationQueue. His example carries out matrix calculations using multiple threads by setting up an NSOperation to evaluate each part of an expression tree.

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

cocoaheads
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

Banner
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

Scott Stevenson has posted Part 2 of his series on Objective-C 2.0. He covers public properties and private setters, custom accessor and variable names, mixing synthesized and custom accessors, and providing methods at runtime.

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

I agree with Gruber. Ars has an excellent behind-the-scenes review. Leopard has a lot of polish and great tools under the hood as well as a nice shine coat on the bodywork.
|

Cocoa: Developer Changes In Leopard

There are many changes in Cocoa beyond the obvious XCode enhancements and brand new Interface Builder. I have had scant time to delve into all of this, but Matt Gemmell has, and he has posted a long list of improvements that have been provided for us behind the scenes.

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

nscodernight
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

Scott Stevenson has encountered what I call the Knowledgeable Novices Syndrome. One of his very helpful and beautifully illustrated tutorials on Quartz garnered a very revealing comment that exquisitely expresses the challenge (and the frustration) of this kind of learner:

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!!!!!!!!!!!!

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 replied thus in the answer to this conundrum:

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.

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.

I'm one of these knowledgeable novices -- not as frustrated as the quoted poster -- but still encountering the same kind of issues.
|

Cocoaheads Meeting October

cocoaheads
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

template-realbasic-header
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

cocoatracescodebeach
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

cocoasmall
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:
rwok344
But if I run the plug-in in French, some of the strings no longer fit:
rwok345
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:
rwok346
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:
rwok347
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

cocoasmall
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:
rwok330
To localize the NoImage.tiff image, I select NoImage.tiff and get Info, then click on Make File Localizable:
rwok335
This changes the image into a group and shows the targets that it is associated with:
rwok336
The Resources have been rearranged like this:
rwok337
Clicking on the General tab shows the languages that the image is localized for:
rwok338
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:
rwok339
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:
rwok340
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:
rwok341
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:
rwok342
And now the image shows up correctly when I run in French or English:
rwok343
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

Pierre Chatelier has posted a PDF guide to Objective C for C++ programmers. It's very good and very complete. I recommend it for anyone who only knows straight C as well because it covers the lesser-know corners of the language that are easily forgotten.
|

Aperture Plugin: Preparing For Localization

cocoasmall
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:
rwok240
is replaced by code like this:
rwok241
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:
rwok321
I get this entry in the text file Localizable.strings created by genstrings for the exporting-images string above:
rwok242
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:
rwok325
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
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

cocoasmall
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:
rwok320
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:
rwok256
The implementation is very simple. I read the first character from the event queue and act on it:
rwok257
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

cocoasmall
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:
rwok314
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:
rwok315
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:
rwok316
And the cached images:
rwok317
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:
rwok318
-updateCachedImages is called when the type of image changes:
rwok319
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

cocoasmall
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:
rwok311
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:
rwok312
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:
rwok313
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

HMBlkAppKit
From the Shiira project comes Aperture-like HUDs supplied in source as a framework.
|

Aperture Plugin: Implementing A Table Of Images

cocoasmall
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.
rw1.0mm
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:
rwok305
I then bound it to the model like this:
rwok306
and set up like this:
rwok307
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:
rwok300
Notice that I actually used a custom class for this. More on that later. The first (image) column is set up like this:
rwok301
To display the image, I drag in an image cell. The properties of the image cell is accessed via the small triangle top right:
rwok302
The inspector shows it as an NSImageCell, set up this way:
rwok303
Now onto the bindings. The first column is bound to the array controller and uses the thumb key to get data from the model:
rwok304
The second column is set up this way:
rwok308
And bound to the text key through the array controller like this:
rwok309

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

cocoasmall
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:
rwok250
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:
rwok251
Initialization is done in the class initializer:
rwok252
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:
rwok253
The level setter code is very simple:
rwok254
To use the logging, I add lines like this to my code:
rwok260
and it provides messages that look like this that include the level, the file name and the line number:
rwok261
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:
rwok130
Clicking on on the Build tab lets me set these up:
rwok131
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:
rwok137
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

cocoasmall
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:
rwok230
Then click on the Info button, and under the Architectures tab, select what I need:
rwok231
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

cocoasmall
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:
rwok220
Then I make sure that my build options for the debug build are set