Чистим Dock и делаем приложение без xCode

c4bnb-qvbkbbsxd8y_75tp_gobe.png

Мы сделаем программу для запуска приложений из панели статуса.
Вам понадобится 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.

kz1zdftsnhs459p5w5ofmwketyy.png

Выйдите из программы нажав в 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..


Для всех методов мы создали кнопки и привязали к ним методы из экземпляра класса 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 '



  CFBundleIdentifier
  example.ToolBar
  CFBundleExecutable
  toolbar
  CFBundleIconFile
  AppIcon

' > ToolBar.app/Contents/Info.plist


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

sudo mv ToolBar.app /Applications/Utilities


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

1ks8djbt9drojjoaxq1_d7ylz_s.png

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

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


echo '



    Label
    example.ToolBar
    ProgramArguments
    
        open
        /Applications/Utilities/ToolBar.app
        --args
        -silent
    
    RunAtLoad
    

' > ~/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 или интерфейс для выравнивания окон на рабочем столе.

© Habrahabr.ru