Мы сделаем программу для запуска приложений из панели статуса.
Вам понадобится terminal, Swift версии 3.1 или выше и любой текстовый редактор.
Я проверял работу на macOS Sierra 10.12.6 и macOS Catalina 10.15.

Открываем terminal /Applications/Utilities/Terminal и создаем файл.

touch toolbar.swift

Открываем файл toolbar.swift и пишем такой код:

import AppKit
var app: NSApplication
var statusItem: NSStatusItem
#if swift(>=5.1)
    app = NSApplication.shared
    statusItem = NSStatusBar.system.statusItem(
        withLength: CGFloat(NSStatusItem.variableLength))
#else
    app = NSApplication.shared()
    statusItem = NSStatusBar.system().statusItem(withLength: CGFloat(32))
#endif

if #available(macOS 10.10, *) {
    statusItem.button?.title = "\u{2699}\u{FE0F}"
}

// extension

// menu

app.setActivationPolicy(.prohibited)
app.run()

Часть кода нужна, чтобы поддерживать совместимость со старыми версиями языка.
Строка app.setActivationPolicy(.prohibited) нужна, чтобы иконка активного приложения не появилась в Dock.

Сохраните файл и запустите программу командой:

swift toolbar.swift

Если нет ошибок, то на панели статуса появится «бесполезная» кнопка. Я использовал UTF символ шестеренки "\u{2699}\u{FE0F}", но можно добавить нужную вам картинку используя свойство statusItem.button?.image.



Выйдите из программы нажав в terminal ctrl-c.

Я решил не создавать class AppDelegate для такого маленького приложения, а расширил класс NSApplication. Добавьте после // extension следующие строки:

extension NSApplication {
    func runTask(_ appName: String, _ arg: String = "") {
        let task = Process()
        #if swift(>=5.1)
            task.executableURL = URL(fileURLWithPath: "/usr/bin/open")
        #else
            task.launchPath = "/usr/bin/open"
        #endif
        task.arguments = arg.isEmpty ?[appName] : [arg, appName]
        #if swift(>=5.1)
            do {try task.run()}
            catch {print(error)}
        #else
            task.launch()
        #endif
    }
    @objc func securityRun () {
        self.runTask("/System/Library/PreferencePanes/Security.prefPane")
    }
    @objc func diskRun () {
        self.runTask( "disk utility", "-a")
    }
    @objc func automatorRun () {
        self.runTask( "automator", "-a")
    }
}

Обратите внимание, что таким образом можно быстрее запускать не только приложения, но и отдельные страницы SystemPreference.

Для методов нужен интерфейс. После // menu наберите:

let menu = NSMenu()
let items: [String] = ["Security", "Disk Utility", "Automator", "Quit"]

var sel: [Selector] = []
let shared = NSApplication.shared

#if swift(>=5.1)
    sel = [
        #selector(shared.securityRun), #selector(shared.diskRun),
        #selector(shared.automatorRun), #selector(shared.terminate)]
#else
    sel = [
        #selector(shared().securityRun), #selector(shared().diskRun),
        #selector(shared().automatorRun), #selector(shared().terminate)]
#endif

for i in 0..<items.count {
    menu.addItem(NSMenuItem(
        title: items[i], action: sel[i], keyEquivalent: ""))
    if i==items.count-2 {
        menu.addItem(NSMenuItem.separator())
    }
}
statusItem.menu = menu

Для всех методов мы создали кнопки и привязали к ним методы из экземпляра класса NSApplication. Мы добавили кнопку «Quit», так как нам надо позволить пользователю выйти из программы без использования terminal.

Компилируем программу. Обратите внимание, что вместо swift надо набрать swiftc.

swiftc toolbar.swift

В текущей директории появился файл toolbar. Его можно запустить командой:

./toolbar

Если мы запустим файл toolbar двойным кликом, то вместе с приложением появится окно terminal, а это не очень удобно.

Попробуем исправить ситуацию. Все команды запускаем в terminal.

Для начала создаем дерево директорий для нашего приложения.

mkdir -p ToolBar.app/Contents/MacOS

Меняем доступ к приложению.

chmod a+x ToolBar.app

Создаем директорию для иконки.

mkdir ToolBar.app/Contents/Resources

Копируем и изменяем имя одной из стандартных иконок.

cp /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/ToolbarAdvanced.icns ToolBar.app/Contents/Resources/AppIcon.icns

Перемещаем скомпилированный файл toolbar в директорию MacOS.

mv toolbar ToolBar.app/Contents/MacOS

Меняем режим доступа к файлу toolbar.

chmod a+x ToolBar.app/Contents/MacOS/toolbar

Cоздаем файл PkgInfo с информацией о типе приложения. Флаг -n нужен, чтобы в файле PkgInfo не было символа перевода строки.

echo -n "APPL????" > ToolBar.app/Contents/PkgInfo

Меняем режим доступа к файлу PkgInfo.

chmod a+x ToolBar.app/Contents/PkgInfo

Создаем минимальный Info.plist с информацией о приложении:

echo '<?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>CFBundleIdentifier</key>
  <string>example.ToolBar</string>
  <key>CFBundleExecutable</key>
  <string>toolbar</string>
  <key>CFBundleIconFile</key>
  <string>AppIcon</string>
</dict>
</plist>' > ToolBar.app/Contents/Info.plist

Перемещаем приложение ToolBar.app в /Applications/Utilities.
Terminal попросит ввести пароль администратора.

sudo mv ToolBar.app /Applications/Utilities

После этих операций новое приложение должно появится в /Applications/Utilities и его можно запустить через Launchpad (почему-то директория с утилитами называется Others)



Осталось добавить наше приложение в автозапуск.

Для этого надо создать файл ~/Library/LaunchAgents/example.ToolBar.plist. Сделаем это одно командной:


echo '<?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>example.ToolBar</string>
    <key>ProgramArguments</key>
    <array>
        <string>open</string>
        <string>/Applications/Utilities/ToolBar.app</string>
        <string>--args</string>
        <string>-silent</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>' > ~/Library/LaunchAgents/example.ToolBar.plist

Проверьте появился ли нужный файл.

ls ~/Library/LaunchAgents

Чтобы проверить автозапуск без перезагрузки, выйдите из приложения и запустите в terminal команду:

launchctl load -w ~/Library/LaunchAgents/example.ToolBar.plist

Иконка приложения должна появится на панели статуса.
Выйдите из приложения и сделайте unload командой:

launchctl unload -w ~/Library/LaunchAgents/example.ToolBar.plist

Теперь приложение запустится при перезагрузке системы, а лишние иконки можно убрать из Dock.

Скорее всего, таким способом можно сделать мини-плеер для iTunes, простой todo list или интерфейс для выравнивания окон на рабочем столе.

Комментарии (5)


  1. dvmedvedev
    18.12.2019 14:10

    Я тоже когда-то пытался писать приложения для панели статуса, а потом открыл для себя bitbar.


    1. veledzimovich Автор
      19.12.2019 14:52

      Отличная штука. Спасибо.


    1. veledzimovich Автор
      19.12.2019 14:54

      Т.е. с помощью это bitbar можно перенаправить выход любого приложения?


  1. BigDrive
    18.12.2019 19:43

    Очень клево, спасибо за статейку.


    1. veledzimovich Автор
      19.12.2019 14:53

      :-)