Archive for January 25, 2015

Sunday, January 25, 2015

An Example on How to Use NSProgress

Jaanus Kase (via Ole Begemann):

The best I can tell, there is no way to do this in Swift that does not look insane and ridiculous. See, -[NSWindowController initWithWindowNibName:] is a convenience initializer on NSWindowController, and Swift is much more restrictive about its initialization behavior in this case. You can’t call the parent’s convenience initializer from your own designated initializer.

[…]

Now, I was able to fix my demo project because it is a small, tightly controlled thing. I can be pretty sure that no other unwanted stuff is running after I fixed the above (first show window, and only then become the current progress object). But this might not be feasible for bigger projects where more things are happening at the same time and you don’t have tight control over all of them. Like, during the time where you are the current progress object, can you guarantee that nobody in your app is going to read anything from disk?

[…]

I suppose another solution is what Ole describes, to forgo the whole “current progress object” business and just pass the parent around between objects, and you can then become a child of that particular one and not just a child of whatever happens to be “current” at that point. This defeats the “loose coupling” aspect, though.

Too bad—it seemed like such a promising API. He says it’s also crashy.

MPW, Carbon, and Building Classic Mac Apps on Yosemite

Steven Troughton-Smith (via Thomas Brand):

mpw is an m68k binary translator/emulator whose sole purpose is to try and emulate enough of Classic Mac OS to run MPW’s own tools directly on OS X. MPW is unique in that it provided a shell and set of commandline tools on Classic Mac OS (an OS which itself has no notion of shells or commandlines) - this makes it particularly suited to an emulation process like mpw attempts to provide, as emulating a commandline app is a lot easier than one built for UI.

[…]

With the same source file, and only a handful of #ifdefs, I could build the same app for 1984’s System 1.0 all the way up to the current release of OS X, Yosemite.

[…]

Right now [mpw is] a fully usable tool that makes Classic Mac OS compilation possible and easy to do on modern versions of OS X, without requiring emulators or ancient IDEs or the like. To my knowledge, this is the first time this has been possible (excluding legacy versions of CodeWarrior).

Update (2015-01-31): Pierre Lebeaupin:

And in fact, it is a very interesting exercise because it allows a better understanding of what makes the Macintosh the Macintosh, by seeing how it was originally programmed for. So I encourage you all to try and play with it.

[…]

QuickDraw is, by some aspects, more approachable that Quartz (e.g. no object to keep track of and deallocate at the end), but on the other hand the Carbon transition added some hoops to jump through that makes it harder to just get started with it; for instance something as basic as getting the black pattern, used to ensure your drawing is a flat color, is described in most docs as using the black global variable, but those docs should have been changed for Carbon: with Carbon GetQDGlobalsBlack(&blackPat); must be used to merely get that value. Another aspect which complicates initial understanding is that pre-Carbon you would just directly cast between a WindowPtr, (C)GrafPtr, offscreen GWorldPtr, etc., but when compiling for Carbon you have to use conversion functions, for instance GetWindowPort() to get the port for a given window… but only for some of those conversions, the others just being done with casts, and it is hard to know at a glance which are which.

Audio Hijack 3

Paul Kafasis:

We’ve been developing products under the Audio Hijack name since 2002, when Mac OS X 10.1 was cutting-edge! Over the years, users have found incredible ways to adapt Audio Hijack for their needs, leading us to make many updates and improvements. Eventually, however, these changes caused Audio Hijack to outgrow our original designs. We’ve incorporated over a decade’s worth of experience and feedback into Audio Hijack 3 to improve the process of recording and enhancing audio. Every aspect of our previous Audio Hijack products has been thoughtfully examined and considered, with enhancements made across the board.

The new interface looks really neat.

Update (2015-02-06): Christa Mrgan:

Having used the node-based applications Quartz Composer and Shake, I imagined Audio Hijack users dealing with a hairy web of randomly-placed, text-heavy rectangles connected by fiddly, intersecting lines and a general sense of chaos.

Even if this style of interface made the application more flexible and powerful, this was not my idea of an improvement. Fortunately for all involved, my protests were ignored. “We’re doing nodes,” Paul said. “We’ll just have to make nodes easier to use.”

And so we worked our way through many different designs. We eventually settled on four basic types of blocks which would automatically snap together when dragged to a grid. The blocks themselves went through many iterations, with various methods for connecting and showing the flow of audio. We finally arrived at the idea of blocks auto-connecting via “pipes”, which would then show a live spectrogram as audio moved through the pipeline from left to right.

Deleting Folders Using the “find” Command

I’ve seen lots of Web pages about how to use the Unix find command to delete files or directories with a certain name. I do this pretty frequently. For example, the Makefile for deploying my apps deletes the Headers folders inside of the embedded frameworks. It also has legacy lines for deleting CVS and .svn folders. The standard advice seems to be to write something like this:

find MyApp.app -name Headers -type d -delete

This looks inside the MyApp.app folder for items named Headers that are directories and deletes them. This works with GNU find, but in the BSD version of find (which ships with Macs) the -delete option only works with files and empty directories. Confusingly, it won’t report failure for a non-empty directory; it will just leave it there. The common solution is to use a command like this:

find MyApp.app -name Headers -type d -exec rm -rf "{}" \;

This executes the rm -r {} command for each found item, with the {} replaced by the item’s path. The \; marks the end of the command and is escaped so that the shell sends the ; to find instead of interpreting it as a command separator. The problem with this version is that it can result in “No such file or directory” errors. find will execute the command for a folder (thus deleting it), then try to recurse into that folder and complain that it doesn’t exist. find succeeded in that all the directories did get deleted—it doesn’t stop when it encounters the first missing directory—but it reports a failing exit status that can halt your shell script or Makefile.

A common way to work around this is to silence the errors. You can use:

find MyApp.app -name Headers -type d -exec rm -rf "{}" \; || true

to make sure that the combined command exits with success. Or, in a Makefile, you can use:

-find MyApp.app -name Headers -type d -exec rm -rf "{}" \;

to tell make to ignore errors. In both cases, the command will succeed, but it will still print “No such file or directory”. So sometimes people silence the error:

-find MyApp.app -name Headers -type d -exec rm -rf "{}" \; 2>/dev/null

My preferred solution is to prevent the error in the first place. You can tell find to do a post-order traversal instead of a pre-order one. In other words, it will recurse first and delete later. This is done by specifying the -d option:

find -d MyApp.app -name Headers -type d -exec rm -rf "{}" \;

You also optimize this somewhat by using + instead of \;. This causes find to send multiple items to a single invocation of the rm command. I’ve also found that, at least with Bash, it is not necessary to quote the braces. This command looks cleaner and works well:

find -d MyApp.app -name Headers -type d -exec rm -rf {} +

Another way to do essentially the same thing is to use xargs:

find -d MyApp.app -name Headers -type d -print0 | xargs -0 rm -rf

In practice, it seems that if you use xargs you don’t need -d. Perhaps this is because a certain amount of find output is buffered before xargs begins deleting. However, I think it is more correct to leave it in.

Update (2015-01-26): As noted in the comments, you can use -prune (instead of -d) to tell find not to recurse into the matching folders that will be deleted anyway:

find MyApp.app -name Headers -type d -prune -exec rm -rf {} +