Михаил Овчинников

Скрываем и показываем иконку приложения в Dock на macOS

Разработка под macOS Swift

Думаю всем очевиден факт, что когда приложение в macOS запущено, оно отображает иконку в доке. Иногда нам это не требуется, к примеру, если подразумевается, что приложение будет работать в фоне, и нет нужды лишний раз напоминать пользователю о его существовании.

В общем случае для этого даже не требуется писать код: достаточно выставить соотвествующий флаг в Info.plist.

 <key>LSUIElement</key>
 <true/>

По-человечески этот ключ называется “Application is agent (UIElement)” и собственно он переведет приложение в режим агента, в котором иконка в доке показываться не будет.

Если же поведение твоего приложения ещё более специфично и есть желание управлять отображением иконки в доке в процессе работы, то можно воспользоваться методом setActivationPolicy класса NSApplication.

Чтобы скрыть иконку (по сути этот метод аналогичен выставлению флага в Info.plist):

 NSApp.setActivationPolicy(.accessory)

Вернуть назад приложение в нормальный режим:

 NSApp.setActivationPolicy(.regular)

Показываем индикатор загрузки файла в Finder

Разработка под macOS Swift

Ты наверное замечал, что при загрузке файлов, например в Chrome, в Finder рядом с файлом показывается некий круговой индикатор загрузки.

Отображение индикактора загрузки файла в Finder

Для этого используется специальный атрибут файла com.apple.progress.fractionCompleted, а также есть небольшая (но неочевидная) хитрость: нужно выставить дату создания файла равной 1984-01-24 08:00:00 +0000 а соотвественно, чтобы скрыть индикатор вернуть файлу актуальную дату и убрать атрибут.

Метод на Swift будет выглядеть следующим образом:

func setProgress(value: Double, forFile path: String) throws {
    var fileAttributes = try FileManager.default.attributesOfItem(atPath: path)
    let extendedAttributesKey = FileAttributeKey(rawValue: "NSFileExtendedAttributes")
    var extendedAttributes = fileAttributes[extendedAttributesKey] as? Dictionary ?? [:]
    let progressData = "\(value)".data(using: .ascii)
    extendedAttributes["com.apple.progress.fractionCompleted"] = progressData
    fileAttributes[extendedAttributesKey] = extendedAttributes

    let dateString = "1984-01-24 08:00:00 +0000"
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "YYYY-MM-dd HH:mm:ss Z"
    let date = dateFormatter.date(from: dateString)
    fileAttributes[FileAttributeKey.creationDate] = date

    try FileManager.default.setAttributes(fileAttributes, ofItemAtPath: path)
}

Пример использования:

try! setProgress(value: 0.6, forFile: "/Users/michael/1.txt")

Скачать пример

Программное добавление папки в «Избранное» в Finder

Разработка под macOS Swift Objective-C

Возникла специфическая задача: добавить программно папку в раздел «Избранное» на боковой панели Finder в macOS. Эта и другая подобная информация хранится в *.sfl файлах, в ~/Library/Application Support/com.apple.sharedfilelist/. Работать с ними напрямую не получится, для этого существует системная утилита sfltool.

Через терминал выполняем следующую команду:

/usr/bin/sfltool add-item com.apple.LSSharedFileList.FavoriteItems file:///Users/$USER/Desktop/

Если нужно произвести такую операцию непосредственно из кода программы, то придется работать с API LSSharedFileList, которое объявлено Apple устаревшим, при этом альтернативных способов сделать это компания не предложила. Применяя этот метод, следует также помнить, что он не будет работать в sandbox-режиме.

На Objective-C:

- (void)appendFavoriteItemWithURL:(NSString *)itemPath {
    NSURL *url = [NSURL fileURLWithPath:itemPath];
    LSSharedFileListRef list = LSSharedFileListCreate(NULL, kLSSharedFileListFavoriteItems, NULL);
    if (list) {
        LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(list,
                                                                    kLSSharedFileListItemLast,
                                                                    NULL,
                                                                    NULL,
                                                                    (__bridge CFURLRef)url,
                                                                    NULL,
                                                                    NULL);
        if (item) {
            CFRelease(item);
        }

        CFRelease(list);
    }
}

Пример использования:

NSString * itemPath = [@"~/Documents/src" stringByExpandingTildeInPath];
[self appendFavoriteItemWithURL:itemPath];

На Swift:

func appendFavoriteItemWithURL(itemPath: String) {
    let url = NSURL(fileURLWithPath: itemPath) as CFURL
    let list = LSSharedFileListCreate(nil,
                                    kLSSharedFileListFavoriteItems.takeRetainedValue(),
                                    nil).takeRetainedValue()
    let startPosition: LSSharedFileListItem = kLSSharedFileListItemBeforeFirst.takeRetainedValue()

    LSSharedFileListInsertItemURL(list,
                                startPosition,
                                nil,
                                nil,
                                url,
                                nil,
                                nil)
}

Пример использования:

let itemPath = NSString(string: "~/Documents/src").expandingTildeInPath
appendFavoriteItemWithURL(itemPath: itemPath)

Источники

Управляем сторонними библиотеками с CocoaPods

Разработка под iOS Разработка под macOS CocoaPods Менеджер пакетов Xcode

CocoaPods — менеджер пакетов, сделанный по образу и подобию RubyGems, позволяющий легко скачивать и подключать сторонние библиотеки к Swift и Objective-C проектам.

Установка CocoaPods

Менеджер пакетов написан на Ruby и распространяется через RubyGems. В общем случае установка сводится к единственной команде в терминале:

sudo gem install cocoapods --pre

Процесс может занять некоторое (продолжительное) время, не выводя ничего на экран, стоит быть к этому готовым и не прерывать установку до завершения (должна появиться надпись вроде «xx gems installed»).

Далее установим мастер-репозиторий CocoaPods со спецификациями всех доступных проектов. Делается это командой в терминале:

pod setup --verbose

Отмечу, что этот шаг тоже занимает достаточно большой промежуток времени, поэтому я указал ключ —verbose, чтобы было видно, что процесс не завис и происходит клонирование репозитория. В зависимости от скорости интернет-соединения, установка мастер-репозитория может занимать от нескольких минут до часа.

Установка и использование библиотек

Чтобы сразу продемонстрировать все возможности создадим новый Swift-проект и установим для него Objective-C компонент под названием BEMCheckBox.

Откроем Xcode и создадим самый простой Single View, Swift-проект, я назову его «CocoaPodsExample». Сохраните его в любое место, закройте Xcode, откройте терминал, перейдите в директорию с проектом и напишите:

pod init

В корне директории должен появиться новый файл: Podfile. Это обычный текстовый файл с конфигурационным кодом на Ruby, в котором мы должны указать требуемые библиотеки. Обычно все солидные opensource-проекты указывают в Readme, что именно нужно прописать в Podfile для установки, и BEMCheckBox здесь не исключение. Откройте его любым текстовым редактором и приведите к следующему виду:

platform :ios, "9.0"

target 'CocoaPodsTest'do
  use_frameworks!

  pod 'BEMCheckBox'
end

Синтаксис должен быть интуитивно понятен: указываем целевую версию платформы, директива use_frameworks! указывает CocoaPods использовать фреймворки вместо статических библиотек (подробности в блоге проекта), а после pod пишем название библиотеки.

Иногда требуется установить не оригинальную библиотеку, а наиболее акутальный форк от другого автора (например, исправление критичного бага, который ещё не успели смерджить) в таком случае можно использовать следующий синтаксис:

pod 'SWRevealViewController', :git => 'git@github.com:safarijv/SWRevealViewController.git', :branch => 'safarijv-additions'

В данном случае мы указываем репозиторий и ветку из которой нужно взять под. Если не указать ветку, то по-умолчанию будет браться master. Аналогично нужно поступать, если вам требуется внести свои изменения в библиотеку: создать форк, закоммитить изменения и сослаться на свой репозиторий в Podfile. Не стоит делать изменения в уже установленных подах, т.к. потом будет очень сложно разобраться (особенно другим разработчикам), почему поведение библиотеки отличается от описанного в оригинальной документации.

Далее запускаем установку командой:

pod install

После окончания установки, в директории с проектом должен появиться файл *.xcworkspace, который объединяет созданный нами проект и проект Pods со всеми необходимыми библиотеками и конфигурационными файлами. Теперь для работы с проектом нужно открывать именно воркспейс, а не сам проект (в противном случае ему не будут видны сторонние библиотеки).

Проверить, что библиотека подключилась легко: перейдите на Storyboard, поместите на ViewController UIView из Object Library и в Identity Inspector поставьте ему класс BEMCheckBox (если не получается, то можно попробовать собрать проект Cmd + B). Запустите проект, и у вас должен появиться работающий чекбокс. Обратите внимание, что мы подключили Objective-C библиотеку к Swift проекту, если вы делали это раньше вручную, то помните, что для этой цели нужно было создавать специальный заголовочный файл Objective-C Bridging Header, но последние версии CocoaPods генерируют его автоматически, поэтому в этом больше нет необходимости.

CocoaPods и системы контроля версий

Обычно хорошим тоном в современной разработке является не добавлять сторонние библиотеки под контроль версий. Однако авторы проекта наоборот, рекомендуют хранить директорию Pods/ вместе с проектом. Преимуществом такого подхода является переносимость проекта и отсутствие зависимости от внешних инфраструктур (например Github, где хранится большинство исходных кодов библиотек). С другой стороны, в крупном проекте со множеством библиотек это может существенно увеличить размер репозитория, а в случае командной разработки добавит головной боли при слиянии веток. Поэтому в небольших проектах, где вы являетесь единственным разработчиком, можно смело оставить директорию Pods/ под контролем версий, а в крупных проектах и при командной разработке, лучше добавить ее в .gitignore.

1 of 1