Home > Apple, Coding, Creativity > HexLauncher: quick apps launch in Mac OS X

HexLauncher: quick apps launch in Mac OS X

The story of this project is taking me back to 5 years to the times when I’ve been just getting started to enter to Apple technologies world, to be more prescice when I was migrating to Mac OS X. As a lot of people I was feeling not too good about a lack of habitual programs and system interface elements. One of such confusion was a miss of Start button equivalent. To be honest I’m still not sure wether the way I’m launching an OS X programs is most traditional (I’m using a Spotlight). So, after few years I deciced to get rid of this disadvantage.

I have to say, I was trying to find an existing solution of this problem for many times but everything I found did’t match my expectations and requirements. All this was leading me to a fundamental conlusion: if you wanna do something – do yourself. So, let it be!
Arming myself with Xcode and internet search tools I started the development. By the way, you can visit a download page for HexLauncher here.
I’ve created a specification first. It’s required to develop an application which puts its icon to a system menu bar so when a user clicks this icon a dropdown menu appears containing a list of favourite applications. When user selects one of the item the associated application is launching. I consider this method much more convenient in compare to Spotlight: when you use a Spotlight you need to operate both mouse and keyboard, from other side HexLauncher lets you start apps just in two clicks which is definitely better in terms of usability. A list of applications is populating by means of settings window which is also avialable from drop down menu. When the settings window is open a list of all available apps in /Applications folder is loaded. User may check a number of apps and that will be a list of quick launch menu. Also it’s required to provide user with ability to run HexLauncher on system start up. That’s all, let’s get started. HexLauncher screenshot
There were just about 5 more or less difficult things in this project for me. There they are.
1. Place an icon to a system menu bar and associate a drop down menu with it. I’m not gonna give a lot of theory now, just will provide a piece of code which was born after a certain period of searching.

- (void)setupMenu {
    NSMenu *menu = [[NSMenu alloc] init];
    //    for (NSString * app in self.apps) {
    NSMenuItem * item = [menu addItemWithTitle:@"HexLauncher Preferences" action:@selector(setup:) keyEquivalent:@""];
    NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:[[NSBundle mainBundle] bundlePath]];
 
    item.image = iconImage;
    [menu addItem:[NSMenuItem separatorItem]];
    int count = 0;
    for (int i = 0; i < self.apps.count; i++) {
        AppItem * app = self.apps[i];
        if (!app.checked)
            continue;
        count++;
        NSMenuItem * item = [menu addItemWithTitle:[app.appTitle substringWithRange:NSMakeRange(0, app.appTitle.length - 4)] action:@selector(launchApp:) keyEquivalent:@""];
        item.tag = i;
        item.image = app.icon;
    }
    if (count)
        [menu addItem:[NSMenuItem separatorItem]];
    [menu addItemWithTitle:@"Quit HexLauncher" action:@selector(terminate:) keyEquivalent:@""];
 
    self.statusItem.menu = menu;
}
 
 
- (void)setupStatusItem {
    _statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
    _statusItem.title = @"";
    _statusItem.image = [NSImage imageNamed:@"StatusItem-Image-rocket"];
    _statusItem.alternateImage = [NSImage imageNamed:@"StatusItem-Image-rocket-alt"];
    _statusItem.highlightMode = YES;
 
    [self gatherAppsList];
    [self setupMenu];
}

I think, it’s quite clear what’s happening here. systemMenu is an object of NSStatusItem class, it’s doing all the magic with placing the icon to system menu bar. gatherAppsList is recursively scanning an /Applications directory and returns an array which is used for menu list.
2. Next difficult moment was displaying an icons of apps. When I was developing my first OS X application (MacMines) I faced this complicated icns format (an Icon set). It was very hard to find how to deal with it and actually how to convert ICON to NSImage. I even found some open source library written in c++. Fortunetely I wasn’t forced to build a library and to integrate it to my project. I’ve found a hundred times simplier way to get NSImage from ICNS – the Cocoa framework has everything what we need. Here it is.

NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:appPath];

appPath is the path to required application (like, /Applications/Skype.app)
3. Launch an application from external applcation also wasn’t something extraordinary or criminal (I was worrying If maybe it could be restricted with security reasons)

NSWorkspace *sharedWorkspace = [NSWorkspace sharedWorkspace];
BOOL didLaunch = [sharedWorkspace launchApplication:app.appPath];

4. It was interesting challenge to show the settings window upon all others (show on top)

- (void)setup:(id)sender {
  [self.mainWindowController.window makeKeyAndOrderFront:self];
  [NSApp activateIgnoringOtherApps:YES];
}

5. The most difficult task was an implementation of Launch the app on system start up. The wide spreaded method which is used by most of applications is so called Launch agents. Mac OS X launch agents are described in details here so we don’t delay for too long in this post. I’d like just to tell a little bit about how it works. Launch agent is created with regular plist file which contains an information about the app itself and a rules of launch (it’s a kind of advanced cron scheduler). This is how my launch agent looks like.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>ru.heximal.HexLauncher</string>
    <key>ProgramArguments</key>
    <array>
        <string>%APP_DIR%/Contents/MacOS/HexLauncher</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

I think any comments are redundant here. I’d just like to say that %APP_DIR% is a placeholder which is replaced with the path to where HexLauncher is currently located. Here is the code to get current app folder.

NSBundle *myBundle = [NSBundle mainBundle];
NSString * bundlePath = [myBundle bundlePath];

There is one more thing I can mention about Launch agents. They can be wether in user’s home directory

~/Library/LaunchAgents/

or in the global library folder

/Library/LaunchAgents/

It’s not too hard to understand that in first case launch agent will work only for exactly that user and in second case the agent will work for any user. That’s all for today.

Categories: Apple, Coding, Creativity Tags: ,
  1. wb77
    April 7th, 2015 at 01:09 | #1

    То есть это, по сути, список любимых и самых важных программ?
    А чем плох док?

  2. wb77
    April 7th, 2015 at 01:15 | #2

    И еще такая мысль пришла: запустить все свои приложения, которыми пользуешься в рабочей среде, нужно только раз – затем мак просто засыпает, и просыпается на той же ноте..)
    Да, утечки существуют, но они накапливаются месяцами…

  3. April 15th, 2015 at 13:13 | #3

    Привет. Да, почти док) но док тоже немного мозг выносит, не информативен за счет того, что непонятно, запущено в данный момент приложение или нет. ну то есть, конечно понятно, там ведь рядом точечка есть, если приложение активно. но бесит, когда случайно тыкаешь иконку в доке, которую не хотел, и при этом запускается какой-то нежелательный апп. особенно, если этот апп тяжелый. поэтому я предпочитаю по минимуму в доке закрепленных аппов держать, в большинстве только те, которые почти всегда запущены) вот для всего остального этот чудесный лончер)
    Есть еще launchpad – но тоже как-то я не проникся им.

    сори за долгий тормоз, после переезда на новый серв как-то почта нестабильно ходит)

  4. August 27th, 2015 at 17:58 | #4

    Здравствуйте!

    Скачал HexLauncher и у меня возникла проблема.
    На странице скачивания написано Requires Mac OS X 10.8 or later
    Во время установки на 10.9.5 (хак) вышло сообщение, что требуется OS X 10.10
    и никак иначе…

    P.S.
    Спасибо за скринсэйвер “матрица” – обожаю его! Вот бы такой под макось (тамошний верик не такой красивший, как Ваш))

  5. August 28th, 2015 at 18:25 | #5

    Здравствуйте.
    упс, действительно, косяк, забыл выставить минимальный таргет в настройках проекта, в ближайшее время пересоберу и отпишусь сюда)
    Спасибо за отзыв, очень приятно.
    Матрица у меня была портирована под мак, и даже работала на 10.5 (Leopard).
    А в следующей версии 10.6 (Lion) Apple ужесточила политику безопасности относительно экранных заставок сторонних производителей, и у меня не хватило терпения разобраться, как это победить)
    Может когда-нить дойдут и до этого руки)

  6. September 22nd, 2015 at 23:04 | #6

    Ещё раз здравствуйте! ))
    Я прошу прощения за свою назойливость – просто хочу узнать, Вы ещё не пересобрали HexLauncher? Или вдруг по прошествии (почти) месяца запамятовали…
    Мы как бы ждём-с :-)

  7. September 23rd, 2015 at 01:30 | #7

    Мильон извинений, дела-дела, суматошное девелоперское бытиё…
    Исправляюсь.
    Выложил билд 1.1
    http://heximal.ru/download/hexlauncher/HexLauncher_1.1.dmg

    Буду премного признателен за тестирование на OS X 10.8
    Собираюсь опубликовать в качестве первого mac-приложение в Mac AppStore

  8. September 26th, 2015 at 12:15 | #8

    Отлично, спасибо!
    У меня самого в распоряжении есть только 10.6.8 и 10.9.6. – однако, мой знакомый с 10.8 уже при деле ))
    Будем тестить.
    Если что найдём, я Вам непременно напишу.

  9. September 26th, 2015 at 13:24 | #9

    Итак, сейчас ситуация такая.

    После запуска на осях 10.8.5 и 10.9.5 лаунчер появляется в процессах, но иконки его сверху нет (не подаёт признаков жизни).
    В 10.11.1 GM запустился успешно, заработал.
    На других версиях OS X мы пока не можем проверить приложение.