Spotlight vs mdfind

Or how to debug why your Spotlight plugin is not finding your files (on Lion).

Let’s say that you’re writing a Core Data, and you’re having a store where you’re saving all your entities (e.g. books). There are a couple of tutorials on how to integrate this Core Data store with Spotlight, for example Marcus Zarra’s presentation “Spotlight and QuickLook vs. Core Data” (NSConference ‘09) or his excellent book Core Data: Apple’s API for Persisting Data on Mac OS X. Basically, each time you’re adding/updating/deleting a row, you are creating/updating a file containing some data you want to index in Spotlight (e.g. book title, description, ISBN, etc).

However, things might not work from the first run. I suggest to first try all the steps from Troubleshooting Spotlight Importers.

If the above steps doesn’t helpe, here are a couple of steps you can also try. Test if your import works with your file:


bash$ mdimport -d2 -g ~/path/to/TestSpotlight.mdimporter ~/path/to/book-isbn-123456.book

There are two important parts in the output of this command. The first one is this:


loading plugin at path '/Users/.../TestSpotlight.mdimporter' for UTIs: com.coralmagnetic.book.

This part says what UTI does your importer know to read (in this case com.coralmagnetic.book).


Import: magic_file returned "XML  document text" for path "/Users/.../book-isbn-123456.book" of type "dyn.ah62d4rv4ge81s7psqvw1e2xtr7zxe"

This part says what type is the file you’re trying to import. In this particular case, it says ‘XML document text’. Because there is a missmatch between the importer plugin and the importer file, this file will be scanned and imported as ‘generic’ file. You will be able to find it using the file name (or extension name), but you won’t find it by looking for content. However if this line says:


Importing type 'com.coralmagnetic.book' using ...

then it should import your content. In case there is a missmatch between these UTIs, you need to check both your Info.plist files (from the App and from the Spotlight importer) for the array key ‘CFBundleDocumentTypes’ to contain key ‘LSItemContentTypes’ with the value ‘com.coralmagnetic.wunderbook’.

Let’s say you made it so far, and the correct importer is reading all the info from your meta file, and somewhere in the output you’re seeing it as:


kMDItemDisplayName = "Alice's Adventures in Wonderland - Lewis Caroll";
kMDItemKind = {
  "" = "Book";
};
kMDItemTextContent = "It tells of a girl named Alice...";
kMDItemTitle = "Alice's Adventures in Wonderland";
kObjectId = "x-coredata://63CB1FB7-9F56-465B-8104-1A0DC8AA383E/Books/p112";

Now, you should try to look for them using Spotlight search (and his command line equivalent: mdfind). If both commands return something, you’re done.

But let’s assume that at an earlier point, as a good MacOSX developer, you’ve decided to save the the files somewhere in the caches directory: ~/Library/Caches/Books/. Running:


mdimport -d2 -g ~/Downloads/Spotlight/TestSpotlight.mdimporter ~/Library/Caches/Books/book-isbn-123456.book 

will return the correct, matching UTIs, but it wouldn’t list the content of the meta file (e.g. the kMDItemTitle key). However, if you move the same “book-isbn-123456.book” file to a different place (e.g. ~/Desktop), the content is properly read. And now is when ‘mdls’ it comes to the rescue, to list the metadata for each specified file.

If you run mdls for the same file, both in ~/Library/Caches/Books and ~/Desktop, you’ll notice a couple of things.

First, in the ~/Library/Caches directory you have this part:


kMDItemSupportFileType = (
  MDSystemFile
)

And in ~/Desktop directory you have this part:


kIsbn        = 12345
kMDItemTitle = "Alice's Adventures in Wonderland"
kObjectId    = "x-coredata://63CB1FB7-9F56-465B-8104-1A0DC8AA383E/Books/p112"

The first part, it tells Spotlight that this is a system file. This means that you won’t be able to find it via the right top Spotlight search (command + space). To find these files, use Finder search, click on the + and add “System files” “are included”.

I don’t know the exact reason that it doesn’t index the files from ~/Library/Caches, but it indexes them from other ~/Library directories. I suppose it has a list of ‘Exclusions’ saved somewhere (it’s not in the regular Spotlight preferences panel). I found this accidentally, and it could be that it wasn’t present before Mac OSX Lion.

Fixing UIAnimator removeAnimationsForTarget crash

You tested your app on the Simulator and everything is fine. You’ve moved it to the iPhone, and when you reach a certain TableViewController your app crashes with the following message: [UIAnimator removeAnimationsForTarget:]. How would you approach this issue? Using common sense, try to set a breakpoint in each function from your TableViewController, then step through them to see in/after which function does the crash happens. In my case, the responsible code was found in the function:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

It entered this function, once for each cell, but it crashed after exiting. As a new iOS developer, I was trying to set a couple of different UITableViewCell in one TableViewController. Here is a sample of the code I was using in the first version:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // Setting a generic cell
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]
                autorelease];
    }
    // ... Other code
    // Replace generic cell with custom cell
    if(objectType == CMMUItemViewSectionSummary){
        NSArray *topLevelObjects = [[NSBundle mainBundle]
            loadNibNamed:@"SummaryCell"
            owner:nil
            options:nil];
        for(id currentObject in topLevelObjects){
            if([currentObject isKindOfClass:[SummaryCell class]]){
                cell = (SummaryCell *) currentObject;
                break;
            }
        }
        cell.summaryData = summaryData;
    } else if(objectType == CMMUItemViewSectionStatuses){
            NSArray *topLevelObjects = [[NSBundle mainBundle]
                                        loadNibNamed:@"ItemStatusCell"
                                        owner:nil
                                        options:nil];
            for(id currentObject in topLevelObjects){
                if([currentObject isKindOfClass:[ItemStatusCell class]]){
                    cell = (ItemStatusCell *) currentObject;
                    break;
                }
            }
        cell.statusesData = statusesData;
    } else {
        cell.textLabel.text = someDefaultData;
    }
    // Return the cell
    return cell;
}

As you can see in the above code, after I was getting a generic cell, I would try to replace it with a custom cell. I guess that somewhere after tableView:cellForRowAtIndexPath: some other piece of code was trying to cleanup the unused cells. Figuring this, it was pretty easy to get it fixed. Using the same sample code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *CellIdentifier = @"Cell"; //Generic identifier
    UITableViewCell *cell;
    // Other code ...
    CellIdentifier = [NSString stringWithFormat:@"Cell%d", objectType]; //cell of my object type
    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(objectType == CMMUItemViewSectionSummary){
        if (cell == nil) {
            NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"SummaryCell"
                owner:nil
                options:nil];
            for(id currentObject in topLevelObjects){
                if([currentObject isKindOfClass:[SummaryCell class]]){
                    cell = (SummaryCell *) currentObject;
                    break;
                }
            }
        }
        cell.summaryData = summaryData;
    } else if(objectType == CMMUItemViewSectionStatuses){
        if (cell == nil) {
            NSArray *topLevelObjects = [[NSBundle mainBundle]
                loadNibNamed:@"ItemStatusCell"
                owner:nil
                options:nil];
            for(id currentObject in topLevelObjects){
                if([currentObject isKindOfClass:[ItemStatusCell class]]){
                    cell = (ItemStatusCell *) currentObject;
                    break;
                }
            }
        }
        cell.statusesData = statusesData;
    }
    if(cell == nil){
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                           reuseIdentifier:CellIdentifier]
                    autorelease];
    }
    return cell;
}

Even if is not the optimal way to use custom TableViewCells, it solves the crash problem. Improvements and suggestions, are welcomed.

On the way to work

Take this morning on my regular cycling to work. I’ve wanted to test the quality of the 4th gen iPod Touch’ camera.

Gh. Lazar Street

GTD and MacOSX Apps

I’ve found out about GTD around 2008 and tempted to try it out, I’ve checked what Macs apps are available for that. Technically not all these apps are 100% GTD but more on the ‘task management’ side.

After a short research I picked OmniFocus at the beginning of 2009. Since I don’t follow all GTD rules, I used is mostly as a “glorified to do list”, installed on the laptop only. I added a couple of tasks and projects and started to tick them off. It had recurrent tasks (which were used to keep track of my haircut schedule), folders/projects (to store lists of appliances/gadgets to buy or the debts like the car loan).

When Andy from Potion Factory launched the first beta of The Hit List, I gave that a try too. I preferred the UI design to OmniFocus’ one. It also had folders/projects and recurrent tasks. It even came up with a setup project meant to showcase the best of it. On the preferences pane it has a tab called iPhone Sync – coming soon. One of my reasons was the hope that the iPhone client would come soon enough (2–4 months) and it would cost less then OmniFocus for iPhone (was hoping for $10). As of today, there is no iPhone app (and no news about it).

In this period of time there was another application available: Things (from Cultured Code). The couple of times when I checked it, it lacked recurrent task and a way to make tasks available only from a certain date on. It didn’t manage to convince me to switch away from THL then. But because THL for iPhone is still in development, about a week ago I decided to give Things another try. Recurrent tasks look nice enough and there is a way to add tasks that are visible only from a certain date on. The prices of the iPhone/iPad apps are in the ranges I’m willing to pay. I got used to the way Things handles tasks (vs THL) and the future looks promising. I’ll give Things a try until the end of August.

I don’t want to complain about Andy’s silence period. He’s a one man indie shop and that makes it easy to throw his work schedule of the track. After all, on my screensaver project things are not moving as fast as I’d like to.

Since it was somewhat clear for me that I should replace THL, I tried to get back to OmniFocus, but I can’t get used to its user interface and work flow.

What I’ve learned from this story is that I should not buy a product based on the promises of “coming soon”. Probably I’d be much happier if I have had paid $20 for OmniFocus for iPhone and use it on a second device for about one year instead of being cheap and switching to The Hit List and now to Things. On the other hand, I really enjoyed these later two programs.

For other interested persons, I’ve attached a comparison screenshot with my pet peeve, recurrent tasks.

OmniFocus recurrent tasks

More Then 100 Percent

Apple and 100%

Speaking of user interfaces, I have found Apple’s secret sauce. They always give more then 100% when working on a product or program. The proof is in the attached screenshot.

/Irony.

I was trying to open all the history links from mobile.de, browsed Saturday. As a side note neither Mac OSX nor Firefox crashed after that, and I managed to visit those pages again (and close them again, one by one).

Snow Leopard wakes from sleep immediately

I’ve noticed that starting with a couple of days ago, the iMac doesn’t want to go to sleep anymore. If I go to Apple menu then Sleep, it tries to go to sleep, but after the hard disk stops spinning, it wakes up again.

After carefully inspecting the Console logs I’ve come up with the following messages:

Feb  8 20:32:17 ... kernel[0]: hibernate_write_image done(0)
Feb  8 20:32:17 ... kernel[0]: sleep
Feb  8 20:32:17 ... kernel[0]: Wake reason = 
Feb  8 20:32:17 ... kernel[0]: System Wake
Feb  8 20:32:17 ... kernel[0]: Previous Sleep Cause: 5

What’s particularly strange is the empty “Wake reason = ” message after sleep. Usually, there you can see different messages like “Wake reason = OHC1”, when you wake the Mac by pressing a key on the USB keyboard.

After removing some innocent Extensions (Remote Buddy and CoolBook2—this one ended up here when I’ve migrated the data from the laptop), I’ve found out the root cause.

In my case, it was the wireless network’s fault. I already have a regular network cable between the Time Capsule and the iMac, but I also have a wireless network for the mobile devices. For some reason, I’ve activated the wireless network on the iMac. This caused the computer to wake up from sleep immediately, but no “Wake reason” was given.

In case your computer doesn’t want to go to sleep too, try this.

Uncluttered, Phase Two

Uncluttered desk

New, smaller, computer requires new desk, which requires new cables setup. Different from the previous setup, I had to cut about 40cm of the board off, ditched the Western Digital MyBook external hard drive (which failed one month after the two years warranty expired). Also I took the opportunity to get rid of the second outlet set.

Attention to details in professional IDEs

About 3 weeks ago I had to switch from Mac OS X (Leopard) to Windows (7 beta build 7100) at the company I work for. One of the reasons of the change was starting of a new project, that will be developed on Microsoft stack (C#, ASP.NET MVC and Microsoft SQL Server 2008). The project will have to be deployed on some Windows Servers, so no point in using C# Mono or PHP.

The first week, was spent getting used to the new platform and all the tools available on it. Installing pasteboard history (ClipX), an Orthodox File Manager (Nomad.NET), a proper editor (GVim for Windows) was something you had to do if you wanted to have a proper work environment.

The second week, I started to take a closer look at the IDE itself, as I already received the sources I was going to work from now on. For a change, it is nice to have code completion for about every namespace, class, method or function, but is annoying not to have word completion (for strange words you have to write inside string declarations like SQL keywords).

Visual Studio 2008 vs XCode

Beside the different change in layout, one of the other things that I noticed was the lack of attention to details in user interface and the general lack of polish. For example, in the above image you can see the subtle blue break point (enabled or disabled) in XCode versus the red break point from Visual Studio. I’d rather have 10 more pixels to fit one or two characters then to have 10% unused space in a window. Also see the subtle indicator for folding area, shades of gray in XCode and square with +/- sign in Visual Studio.

A much more visible differences in Visual Studio are the items from the menu item and the layout of the sub-windows that change when you go from Edit mode into Run/Debug mode and back. It is distracting to see shapes changing in background when you should concentrate on the front window. Better to keep everything fix and in place, and let you focus on the things you’re doing.

These are the touches that makes most of Mac OS X software much more enjoyable then most of the Windows software.

The Teapot Bell

The bicycle teapot bell

During my business trip to Munich I haven’t had much time to wonder around and visit bike shops (although I bumped into a big one near Theresienwiese U-Bahn station), that’s why I’ve ordered this nice bicycle teapot shaped bell from Amazon.de. I think that it’s nice form goes much better with my city bicycle then a regular one. I’m sure that the nice and shiny look will wear out pretty soon.

Productivity on Mac

As many office workers, I got infected with the ‘productivity bug’.

I found myself using two types of software daily (or at least weekly) on Mac (that I didn’t use while I was using Linux).

The first type is task management, represented by OmniFocus, which helps me not to forget different task/errands.

For what I use OmniFocus?

  • List of monthly rates I have to pay (car loan, assurance fees)
  • List of important things (utilities) or less important (having a haircut) I have to pay from time to time
  • List of things I’d like to buy in the new future (inline skates, shoes)
  • List of things to do (home errands)

For what I don’t use it?

  • Managing projects, although some projects are written down there, but I don’t work on them much.
  • Grocery store lists. For that I use a small post it on the fridge, where I write what I should not forget to buy on the next trip to grocery store.

Also not to forget dates/meetings/events I use iCal, the build in calendaring software. I prefer this to OmniFocus because it syncs with my iPods and with my mobile phone. OmniFocus can sync some of it’s entries to iCal too. I’ve tried this feature once, but I think I put all my to-dos in one calendar and that it become littered with entries from OmniFocus. On a screencast I’ve seen that you should sync only one of your contexts (e.g. errands) with one of the calendars, not all the contexts with one calendar (as I did).