20111221

Developments

Since my previous post, I have changed employers twice. First to SMSI, but I didn't feel like I was doing meaningful work there. I now work for Amazon on the Kindle Team. I find that I prefer C++ and Objective-C to Java, but I am willing to admit that it could be due to lack of understanding of the inheritance model.

20110616

Unbundled Info.plist on 10.5 Leopard

We recently added a non-privileged, unbundled (stand-alone) helper tool to our main application bundle. At first it showed up in the Dock, but then we added LSUIElement to that application's Info.plist. Naturally, we embed that plist with -sectcreate __TEXT __info_plist Info_plist_file.
However, 10.5 did not respect this setting. It insisted on displaying a duplicate Dock tile for the main app -- same name and icon. We tried several variations, but the only thing we found that works on 10.5 is to make the helper application a Cocoa app bundle and include its Info.plist there. Only then is the LSUIElement preference respected.

20110103

App Distribution

Everyone wants to have users download a small .dmg file from their website. Opening the dmg pops up a window showing a pretty background, the application's icon, and an alias file to the /Applications directory.

Finder in 10.6 creates non-backward compatible .DS_Store files, so to get the positioning and background image, you need to create a dummy dmg by hand on 10.5, copy its .DS_Store, and save it for copying into your production dmg.

No-one wants their dmg to have a .Trashes directory, and no-one wants the mounted image to have free space -- it's just wasteful, and it feels unprofessional. The key to creating just such a dmg is to populate a local directory with everything you want in your dmg, use hdituil to makehybrid from it, and then convert to the desired format (probably UDBZ format with -imagekey bzip2-level=9 for maximum compression).

If you have an SLA that you want to display, you can unflatten, add the rtf SLAs using Rainer Brockerhoff's AddLicense tool, and then flatten the dmg again.

20100902

Google Chrome

Until now, I have been using Google Chrome web browser. I manually removed its Keystone updater launchd jobs and executable. However, I stopped using it today because it stopped showing me the uri of the webpage I was viewing. This is unforgiveable.
The browser is not supposed to guess that there is some information I don't want.

20100729

Installing a Helper Tool

If your application does something with/to files that the user wouldn't normally have access to, you can use IPC (inter-process communication) with an Elevated Helper Tool. But how do you install the Helper Tool?

Prior to OS X 10.6, you would have to use a Helper Tool Installer, which you would have to launch using AuthorizationExecuteWithPrivileges(...). The (authorized) Installer would also have to install a launchd plist and register the plist using launchctl.

On 10.6 "Snow Leopard", Apple has made this process easier with the Service Management APIs. There is one API now, SMJobBless, that takes your authorization and the name of the helper tool, and installs it. Here are some of the details of using it.

Both the application and the Helper Tool must be code signed. It isn't necessarily important who is the CA.

The Helper Tool must reside inside of your application bundle in the Contents/Library/LaunchServices directory. The name of the HelperTool executable file MUST be its launchd Label. This is usually a reverse-DNS label such as com.QuantumCheese.MyApp.HelperTool .

Furthermore, SMJobBless requires two custom sections in the __TEXT segment of your executable. The first of these is the info plist. This is a normal Info.plist file, customized for your Helper Tool. It must contain an array SMAuthorizedClients of code signing requirements for authorized clients. It is also a good idea to have a version string (the key is CFBundleVersion) and a bundle name (key CFBundleIdentifier) (probably the Label).
The second is the launchd plist. This is the same plist you would have written from the Installer executable; it ends up in /Library/LaunchDaemons. See man (5) launchd.plist for information on this file; it must include the Label, and it should not include Program or ProgramArguments keys (they will be ignored and replaced).

In the Xcode project settings for your helper tool's target, add Other Linker Flags of
-sectcreate __TEXT __info_plist /path/to/your/daemon's/info.plist
and
-sectcreate __TEXT __launchd_plist /path/to/your/daemon's/launchd.plist
Note that these two lines will be split into eight lines; make sure that each 4 stay together.

The documentation for SMJobBless says that the only domain currently supported is kSMDomainSystemLaunchd. Therefore, SMJobBless will take your Helper Tool, extract its __launchd_plist to /Library/LaunchDaemons/reverse-DNS-HelperTool.plist, and copy the Helper Tool to /Library/PrivilegedHelperTools/reverse-DNS-HelperTool. It automatically version checks, and the API returns true on successful upgrade or if the tool is already installed.

Experiments indicate that it will return true even if a newer version of the Helper Tool is installed. Therefore, if your IPC demands that your bundled version exactly matches the installed version, you will still need to use the Helper Tool Installer to downgrade.

Even though it is tempting, I recommend against putting an additional copy of the executable into the Contents/MacOSX folder. While doing so would let you use the CFBundleCopyAuxiliaryExecutableURL API to create a URL to the helper executable, it increases the size of your bundle, which is a bigger problem than finagling the helper executable's URL on 10.5.
Instead, I recommend appending the Contents/Library/LaunchServices/HelperTool path to the URL obtained from CFBundleCopyBundleURL(CFBundleGetMainbundle()).

20100602

Ways to Bork Your Code

Maybe I'll make this a series of posts: ways to screw up, big time.

Preprocessor macros are very handy, but very easy to abuse. One day in college, a few of us sat around trying to think of devious macros we could include in our projects that would mess up other contributors' code.
We tried #define 1 (-12), but it wouldn't compile.
We did succeed in #define main foo to eliminate the main function, but my personal favorite takes two lines:

#define if while
#define else


And of course, to be a good hygienic programmer, you'd want to include the accompanying #undefs. Please never use this.
I can just imagine some confused code successor looking at the file and asking, "Why does this file end with #undef if? Uhh....."

20100530

NSMenu popup location

NSMenu has a class method popUpContextMenu:withEvent:forView:, which I initially thought was the only way to cause a menu to pop-up where I wanted. In order to give it a useful event, I took the event I had and created a new one from it, with a different mouse location, to try to trick the menu's position. The goal is that the menu should normally appear under the control, with the left of the menu aligned to the left of the control. If there isn't enough room under the control, the menu should appear above the control and not obscure it from view. Similarly, if there isn't enough space to the right of the control for the entire menu to display, the menu should right-align to the control.
I had gotten the above/below positioning sorted out using the NSMenuDelegate method confinementRectForMenu:onScreen:, but I could not get the left/right alignment switch to work. If I would set the menu rect's origin to the left of the control, the upper-right of the menu would be there; and if I set it to the right of the control, the menu's upper-left would be there.
I put the menu to the left of the control entirely and then moved the x field of the origin over by the width of the menu. No good.
I tried moving it over incrementally over several test runs. It eventually became clear that I could not move the x coordinate of the menu's origin past 5/6 of the width of the control without the menu aligning its left to the left of the control.

I then discovered that NSMenu has an instance method popUpMenuPositioningItem:atLocation:inView:. This method had much better documentation, saying that I could pass nil for the item and the view, and then I could control the upper-left corner of the menu rect, and it would pop-up unattached to any other window. I tried this out, and it worked, with less code (no longer needed to fake an NSEvent).
I went back and used a nil view for the class method, and this also worked.

My recommendation is to use the class method, since it provides the flexibility to position an arbitrary item in the menu or the entire menu at a certain point, and it has much better documentation.

20100429

Apple and Adobe

My response to Steve Jobs' mostly well-written reasons for not allowing Flash on his mobile devices.

Jobs is spot-on that Flash is a closed, proprietary system. It renders video (and other content) in software, not hardware; he claims that all mobile devices decode H.264 in hardware, and can play 10 hours of H.264 video content vs. 5 hours of software-decoded video. I haven't tested his claim, but specialized hardware seems like it ought to do the job better.

The part that really bothers me is where he attacks Adobe's iPhone programming interface: "We know from painful experience that letting a third party layer of software come between the platform and the developer ultimately results in sub-standard apps and hinders the enhancement and progress of the platform."
The "progress of the platform" is nonsense. It is up to the developers to use whatever tools they chose to create the products they desire. It is not relevant to Apple what methods we, the developers, use to create our content.

Furthermore, Steve Jobs has no right to complain that Flash is a closed system when his App Store is even more restrictive. It would be better to have an Open app store and allow developers to use whatever tools we want!

Jobs complains that "[t]he third party may not adopt enhancements from one platform unless they are available on all of their supported platforms. Hence developers only have access to the lowest common denominator set of features. Again, we cannot accept an outcome where developers are blocked from using our innovations and enhancements because they are not available on our competitor’s platforms."
Again, this is better than Apple's practice of changing the APIs out from under developers. Their attitude is one of "We're so very glad that you used our APIs, but we've deprecated them all, so you'll have to rewrite your code, at least to have dynamic OS-testing," but they say it differently (from the article): "We want to continually enhance the platform so developers can create even more amazing, powerful, fun and useful applications. "
This is a terrible methodology, and it's a terrible way to treat programmers.

20100414

Creating a DMG with an icon and background image

[Posted with permission from my client.]

Our Release mode build is designed to package up the final app bundle into a dmg. We wanted to make a nice user experience, similar to the images of Google Chrome and Firefox, so we added an alias to /Applications and set the alias' name to " ".
We use an applescript to set the position and size of the icons in icon view, and also to set the background image. However, we could not figure out how to set the icon for the mounted volume in Finder's sidebar. I knew that it involed .VolumeIcon.icns, but that was insufficient.
After much searching, we realized that we had to set extended Finder attributes for the volume's root directory, but couldn't figure out how to do so from the shell / applescript. Finally, we found xattr, but there was no documentation to indicate what names or attributes to use. However, Google Chrome and Firefox both had the same set of 32 4-byte hex flags, so we simply copied them.

Solution: with read/write dmg mounted (and a disk name of VOLUME_NAME), use the following command from the Terminal (or shell script):

xattr -wx com.apple.FinderInfo 00000000000000000400FFFFFFFF000000000000000000000000000000000000 "/Volumes/${VOLUME_NAME}"


UPDATE:
There is a much more intuitive interface: SetFile -a C "/Volumes/${VOLUME_NAME}".

20100224

Specifying a port on IPv6

My colleagues at Javacool Software got a feature request today for an existing product, that it should run on a custom port, presumably due to the client's unusual network setup.
While doing that, we decided that it makes sense to support this feature for IPv6 as well. However, it turns out that specifying a port on IPv6 is much more annoying that on v4. In v4, the format is like so: dot-separated-IP:port. In v6, like so: [colon-separated-IP]:port.

20100212

Source Control

Until now, the CFRaii library has been available from my website. Now it is also publicly available through github. This is a trial for me, to see if I like git and github. I'll let you know.

20100127

NSThread

The project I'm working on uses threads. Like a good Cocoa app, the main thread is in charge of the GUI, and the worker threads run in the background so that we don't pinwheel.
In order to know when the thread has finished its work, we registered for notification:

[[NSNotificationCenter defaultCenter] addObserver:self
selector:aSel
name:NSThreadWillExitNotification
object:thread];


and in the aSel method, use ivars to update an NSTableView's dataSource.

We had a really pesky race condition that, after two days, we realized was caused by aSel running in the same worker thread! It turns out that this is in the documentation, that notifications are served to the same thread that posts them. However, this is a fairly useless implementation decision.

Our workaround is not to register for the notification, but rather to have the thread performSelectorOnMainThread:withObject:waitUntilDone: at the end of its execution. There is still a problem that we'd like to release the thread at that point, but we don't know what will happen if we try that!

20100108

Apple, part II

The same day I received the rejection email from Apple, a different Apple recruiter contacted me on LinkedIn and asked me if I would be interested in a job at Apple. I wanted to see where it would go, so I said sure, tell me about the job. He described a position with the security testing team for all Apple products, but only very broadly. When I asked some detailed questions, he couldn't answer them, so he offered to set up a phone call with the team leader.
About 1.5 weeks after being contacted on LinkedIn, I got a call from the team leader. He described the position, and it sounded like something I'd enjoy, so I asked him how to apply for it; should I send a resume, etc. He said he had something like a resume in front of him (which I assume the recruiter copied from LinkedIn), and that I didn't have the experience or background they wanted, unless there was something I'd left off my resume.
I asked him why, if he had already decided that I didn't have the background he wanted, did he call me? He told me that he'd called me because the recruited had asked him to, because the recruiter couldn't answer my questions.

20091202

Apple, part I

In June of 2009, I applied to work at Apple. A few weeks later, I got a call from an Apple recruiter, and we scheduled a first-round phone interview. I passed that one, and he called to schedule a second-round phone interview. I passed that one, too, and he called to schedule an in-person third-round interview.
By now it was mid-August. I took a day off of work to interview with 6-7 different Apple engineers, each for 45 minutes. I talked with them, showed them what I knew and what I could do in terms of coding and theory. The group leader told me I'd hear back by the end of the week. I thanked them, and went home.
I made a mistake, in that I didn't immediately send a "thank you" email; I waited a few days to do so.
The week ended, and I hadn't heard anything. I thought perhaps I misheard, and he'd said the end of the following week. Still nothing. No phone call, no email.
The next week, now 2.5 weeks after my interview, I emailed to ask if there was anything I could give them -- references, or further background, or anything -- to help them decide. The following was the entirely of the response I received, without the names:

Sorry [me]. Unfortunately, the team decided to pass on your candidacy.

[recruiter]

20090731

Core Foundation meets C++

I am tired of dealing with Core Foundation ornerinesses in a program primarily written in C++, so I decided to write a library of RAII wrappers for Core Foundation classes I use commonly.

My first problem was that I didn't really understand about overloading assignment operators, but I think I've figured them out now.

The next concern is that for many types, there are mutable and immutable version, which differ only by a const in their typedef to the opaque underlying type. My first thought was to implement, for example, CFMutableArrayRef and CFArrayRef wrappers separately.

My next thought was to set up some kind of inheritance.
Inheritance fails, however. If we want to say that the wrapper for the immutable inherits from the mutable, and makes some member functions/operators private, then we'll run into the problem that an instance of a derived class must be able to function as an object of the base class, and a derived class wrapping an immutable object cannot act the same as a base object that is mutable.

Third try: Make the private data member mutable, but provide a const "conversion operator" that will make it act like it's immutable. This seems like the best option so far.

I am planning to make the library available once it's usable. I am still deciding on the licensing system I wish to use, but I think I am leaning toward BSD-style, or maybe Creative Commons.

20090726

C string to CFString

I have a null-terminated string of (random) ASCII chars that I need to convert to a CFString for an API call. However, the following call yields NULL:

CFStringRef convertedValue = CFStringCreateWithCString(kCFAllocatorDefault, c_string, kCFStringEncodingUTF8);

According to the doc, this means "there was a problem creating the object."
I am currently stumped as to why.

20090715

launchd confusion

Does the following documentation indicate that one may not fork()/exec() in a launchd process? I'm certainly getting strange console messages when I do, but it's not clear yet if that's due to malformed code or if it's because of the way launchd treats children of children processes.

Citation: launchd documentation

Creating and running a launchd job

Whether you're migrating a job to launchd from some other system job launcher, or you're setting up a job for a new service, there are a few basic requirements for the job.

Jobs run from launchd should not duplicate launchd functionality; for instance, they should not use chroot(2). Furthermore, they should not do the things normally required of daemon processes, such as detaching from the terminal they are initially attached to. The only things that are strictly prohibited, however, are fork()/exit() combinations (including indirect methods, such as the daemon(3) library call). A server which attempts to run itself as a daemon in this way will seem to have finished running, potentially leading to launchd respawning it, or disabling the service. As launchd does not get stalled waiting for a child that hasn't yet exited, it's not necessary to try to prevent it.

20090714

CFFork

It is absolutely forbidden to use Core Foundation or higher-level APIs in a child process after fork()ing, before exec*()ing. This royally messes up our process model.

citation: OS X Leopard Core Foundation Release Notes

CoreFoundation and fork()
Due to the behavior of fork(), CoreFoundation cannot be used on the child-side of fork(). If you fork(), you must follow that with an exec*() call of some sort, and you should not use CoreFoundation APIs within the child, before the exec*(). The applies to all higher-level APIs which use CoreFoundation, and since you cannot know what those higher-level APIs are doing, and whether they are using CoreFoundation APIs, you should not use any higher-level APIs either. This includes use of the daemon() function.

Additionally, per POSIX, only async-cancel-safe functions are safe to use on the child side of fork(), so even use of lower-level libSystem/BSD/UNIX APIs should be kept to a minimum, and ideally to only async-cancel-safe functions.

This has always been true, and there have been notes made of this on various Cocoa developer mailling lists in the past. But CoreFoundation is taking some stronger measures now to "enforce" this limitation, so we thought it would be worthwhile to add a release note to call this out as well. A message is written to stderr when something uses API which is definitely known not to be safe in CoreFoundation after fork(). If file descriptor 2 has been closed, however, you will get no message or notice, which is too bad. We tried to make processes terminate in a very recognizable way, and did for a while and that was very handy, but backwards binary compatibility prevented us from doing so.

20090707

cfstring parsing

Why are strings so annoying?

I'm trying to take integer values from a c-string, but only if it's well-formed. Using CFNumberFormatterGetValueFromString works if I can correctly determine the boundaries of the number portions. The difficulty is determining where those are. The formatter still gives a value if the range contains, for example, 5e. The desired behavior is for it to return false is such a situation.

Can anyone recommend a better way to parse a CFString, so that I can determine if the string is well-formed, and get the values if so?

Edit: traversed the c-string, finding ranges and delimeters by hand.

20090628

Elevated Processes

Objective-c is an insecure language. Although the feature is being deprecated (I believe starting with Snow Leopard), a user may override a program's code.

An application that needs to perform a restricted action can prompt the user for Administrator password using the Security framework. However, for security reasons, the best methodology is to have a helper application, and launch it using the authorization obtained. This raises issues of setting +s on the helper tool and interprocess communication. Allegedly, all of these are very straightforward, but the Apple-provided BetterAuthorizationSample project is written in somewhat arcane C (there are goto statements!), and I find it very difficult to understand what's going on.

I would appreciate assistance, especially in the form of a non-Apple tutorial or documentation, indicating how to set up communication with an executable that is a secondary target in the same xcodeproj.

EDIT: Have now gotten helper application to launch. There was a typo in the filepath I passed to AEWP.