Mixing Cocoa and Qt

Qt does a great job at abstracting out platform-specific features into platform-independent C++ APIs. However, sometimes you still need to write platform-specific code for features that are not in Qt (e.g. to access the platform’s address book),  or to access platform-specific applications (e.g. iTunes) or libraries (e.g. Sparkle). On Mac OS X, almost all interfaces are offered through the Cocoa Objective-C interface, and the interfaces that are written in C++ have been deprecated and will disappear soon in favor of Cocoa. Although the language of Cocoa is different from Qt’s, Qt and GCC make it very easy to call these interfaces from within your application. In this post, I will show how this can be done by making an auto-updating application using Sparkle.

To create our auto-updating application, we first lay down the auto-updater interface in a (pure) virtual class:

class AutoUpdater {
  public:
    virtual void checkForUpdates() = 0;
};

The Sparkle implementation of this interface has the following C++ header:

class SparkleAutoUpdater : public AutoUpdater {
  public:
    SparkleAutoUpdater(const QString& url);
    ~SparkleAutoUpdater();
    virtual void checkForUpdates();
  private:
    class Private;
    Private* d;
};

Note that I am using the Pimpl idiom to hide Cocoa-specific internals from the API into a private internal class.

The actual impementation of the SparkleUpdater needs to call Sparkle’s Objective-C API. However, we cannot write the implementation in Objective-C, since the header for our updater (which will be included from within our application) is written in C++. Luckily, GCC’s Objective-C++ extension allows you to mix Objective-C with C++ without any problem. So, we create an Objective-C++ implementation of our updater, and put it in a .mm file:

class SparkleAutoUpdater::Private {
  public:
    SUUpdater* updater;
};

SparkleAutoUpdater::SparkleAutoUpdater(const QString& aUrl) {
  d = new Private;
  d->updater = [[SUUpdater sharedUpdater] retain];
  NSURL* url = [NSURL URLWithString:
      [NSString stringWithUTF8String: aUrl.toUtf8().data()]];
  [d->updater setFeedURL: url];
}

SparkleAutoUpdater::~SparkleAutoUpdater() {
  [d->updater release];
  delete d;
}

void SparkleAutoUpdater::checkForUpdates() {
  [d->updater checkForUpdatesInBackground];
}

All we do here is create an SUUpdater (Sparkle’s updater class), set it up, and activate it in our checkForUpdates() method.

To integrate Objective-C++ classes into a Qt project, qmake provides the OBJECTIVE_SOURCES variable. Knowing this, we add our new classes to our qmake project file:

HEADERS += AutoUpdater.h
SOURCES += AutoUpdater.cpp
mac {
  HEADERS += SparkleAutoUpdater.h
  OBJECTIVE_SOURCES += SparkleAutoUpdater.mm
  LIBS += -framework Sparkle
}

Before we integrate the updater into our application, there is still one detail we have to take care of: if you write Objective-C code that creates objects, you need to instantiate an NSAutoReleasePool to enable Cocoa’s memory management. Although we could do this in SparkleAutoUpdater, we’re going to put this in a separate initializer class, so we can share this with other Objective-C++ implementations in our application. The body of this class looks as follows:

class CocoaInitializer::Private {
  public:
    NSAutoreleasePool* autoReleasePool;
};

CocoaInitializer::CocoaInitializer() {
  d = new CocoaInitializer::Private();
  NSApplicationLoad();
  d->autoReleasePool = [[NSAutoreleasePool alloc] init];
}

CocoaInitializer::~CocoaInitializer() {
  [d->autoReleasePool release];
  delete d;
}

Now all that’s left to do is integrate all this into our application:

int main(int argc, char* argv[]) {
  ...
  AutoUpdater* updater = 0;
#ifdef Q_WS_MAC
  CocoaInitializer cocoaInitiarizer;
  updater = new SparkleUpdater("http://el-tramo.be/myapp/appcast.xml");
#endif
  ...
  if (updater) {
    updater->checkForUpdates();
  }
  ...
}

That’s it! If you want to try this out yourself, just download the sources of this mini-project, build them, and off you go. Don’t forget to remove ~/Library/Preferences/be.el-tramo.mixing-cocoa-and-qt.plist to clear Sparkle’s state cache.

This page was posted in Programming with tags Programming, C++, Mac OS X, Cocoa, Qt, Objective-C, Sparkle, GCC .