Swift - запуск приложения Mac для запуска при запуске
В течение последних нескольких недель я пишу приложение для Mac в быстром режиме, как для программирования на Mac, так и для быстрой работы, когда мы переходим к нему на моем рабочем месте. В настоящее время я пытаюсь получить код для добавления моего приложения в качестве приложения "Запуск при запуске", пошив код, любезно предоставленный на BDungan blog
До тех пор, пока я много часов возился, я придумал следующее:
func itemRefInLoginItems () -> LSSharedFileListItemRef?
{
var itemRef: LSSharedFileListItemRef? = nil
var itemURL: Unmanaged<CFURLRef>?
let appURL = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath)
if let loginItemsRef = LSSharedFileListCreate(kCFAllocatorDefault,kLSSharedFileListSessionLoginItems.takeRetainedValue(),NSMutableDictionary()) {
var unretainedLoginItemsRef = loginItemsRef.takeUnretainedValue()
if var loginItems = LSSharedFileListCopySnapshot(unretainedLoginItemsRef, nil) {
for item in (loginItems.takeRetainedValue() as NSArray) {
let currentItemRef = item as LSSharedFileListItemRef
var outRef: FSRef
if (LSSharedFileListItemResolve(currentItemRef, 0, &itemURL, nil) == noErr) {
if (appURL?.isEqual(itemURL?.takeRetainedValue()) != nil) { //PROBLEM 1
itemRef = currentItemRef
}
}
}
}
}
return itemRef
}
func isLaunchAtStartup () -> Bool {
let itemRef = self.itemRefInLoginItems()
return itemRef != nil
}
func makeLaunchAtStartup () { // Compile seems to fall down on this line...
if !self.isLaunchAtStartup() {
let loginItemsRef = LSSharedFileListCreate(kCFAllocatorDefault, kLSSharedFileListSessionLoginItems.takeRetainedValue(), NSMutableDictionary())
let appURL = NSURL(fileURLWithPath: NSBundle.mainBundle().bundlePath) as CFURLRef
let itemRef = LSSharedFileListInsertItemURL(loginItemsRef.takeRetainedValue(), kLSSharedFileListItemLast.takeRetainedValue(), nil, nil, appURL, nil, nil)
}
}
Я столкнулся с двумя проблемами.
Проблема 1
Swift не хочет, чтобы я сравнивал NSURL с CFURLRef... теперь я пошел с предложением Xcode только для того, чтобы запустить приложение, но я на 100% уверен, что он не делает то, что я думаю, это, (см.//ПРОБЛЕМА 1).
В objective-c кажется, что между NSURL и CFURLRef (или CFURL) разрешено использовать бесплатный мост, однако при попытке выполнить бросок, условный листинг или любое другое быстрое, как (вставить правильный символ здесь) приближается к моему коду, неизбежно не будет строить. Я получаю такие ошибки, как:
Неуправляемый не является подтипом NSURL:
if appURL as Unmanaged<CFURLRef> == itemURL
e.t.c.
Проблема 2
Хотя этот код в настоящее время не дает никаких предупреждений или ошибок... при попытке скомпилировать, я получаю Command failed due to signal: Segmentation fault: 11
, который, если быть откровенным... находится вне меня.
Ответы
Ответ 1
Мне удалось получить рабочую реализацию этого построения на основе подхода Objective C Брайана Дунагана.
Я также столкнулся с проблемой сбоя в компиляторе seg, но это было вызвано попытками приведения к недопустимым типам; получение типов прав устраняет проблему.
Я не смог получить kLSSharedFileListItemLast
, чтобы правильно вернуть ссылку на последний элемент файла, поскольку он всегда вызывает ошибку seg. Чтобы обойти эту проблему, я изменил функцию itemReferencesInLoginItems
, чтобы вернуть кортеж ссылок на объекты.
Первый элемент в кортеже - это существующая ссылка на приложение, если она существует, второй элемент является последней ссылкой в списке. Используя этот подход, мы можем не полагаться на kLSSharedFileListItemLast
.
Вот код, не стесняйтесь его использовать! Мне бы хотелось узнать, есть ли способ заставить kLSSharedFileListItemLast
работать.
func applicationIsInStartUpItems() -> Bool {
return (itemReferencesInLoginItems().existingReference != nil)
}
func itemReferencesInLoginItems() -> (existingReference: LSSharedFileListItemRef?, lastReference: LSSharedFileListItemRef?) {
var itemUrl : UnsafeMutablePointer<Unmanaged<CFURL>?> = UnsafeMutablePointer<Unmanaged<CFURL>?>.alloc(1)
if let appUrl : NSURL = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
let loginItemsRef = LSSharedFileListCreate(
nil,
kLSSharedFileListSessionLoginItems.takeRetainedValue(),
nil
).takeRetainedValue() as LSSharedFileListRef?
if loginItemsRef != nil {
let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
println("There are \(loginItems.count) login items")
let lastItemRef: LSSharedFileListItemRef = loginItems.lastObject as LSSharedFileListItemRef
for var i = 0; i < loginItems.count; ++i {
let currentItemRef: LSSharedFileListItemRef = loginItems.objectAtIndex(i) as LSSharedFileListItemRef
if LSSharedFileListItemResolve(currentItemRef, 0, itemUrl, nil) == noErr {
if let urlRef: NSURL = itemUrl.memory?.takeRetainedValue() {
println("URL Ref: \(urlRef.lastPathComponent)")
if urlRef.isEqual(appUrl) {
return (currentItemRef, lastItemRef)
}
}
} else {
println("Unknown login application")
}
}
//The application was not found in the startup list
return (nil, lastItemRef)
}
}
return (nil, nil)
}
func toggleLaunchAtStartup() {
let itemReferences = itemReferencesInLoginItems()
let shouldBeToggled = (itemReferences.existingReference == nil)
let loginItemsRef = LSSharedFileListCreate(
nil,
kLSSharedFileListSessionLoginItems.takeRetainedValue(),
nil
).takeRetainedValue() as LSSharedFileListRef?
if loginItemsRef != nil {
if shouldBeToggled {
if let appUrl : CFURLRef = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
LSSharedFileListInsertItemURL(
loginItemsRef,
itemReferences.lastReference,
nil,
nil,
appUrl,
nil,
nil
)
println("Application was added to login items")
}
} else {
if let itemRef = itemReferences.existingReference {
LSSharedFileListItemRemove(loginItemsRef,itemRef);
println("Application was removed from login items")
}
}
}
}
Ответ 2
удивительный ответ, но вы забыли оператор if, чтобы проверить, не позволяет ли ошибка отклонить пустое значение, если нет элементов
Вот быстрое решение для одной функции, надеюсь, что это поможет кому-то
func itemReferencesInLoginItems() -> (existingReference: LSSharedFileListItemRef?, lastReference: LSSharedFileListItemRef?) {
var itemUrl : UnsafeMutablePointer<Unmanaged<CFURL>?> = UnsafeMutablePointer<Unmanaged<CFURL>?>.alloc(1)
if let appUrl : NSURL = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
let loginItemsRef = LSSharedFileListCreate(
nil,
kLSSharedFileListSessionLoginItems.takeRetainedValue(),
nil
).takeRetainedValue() as LSSharedFileListRef?
if loginItemsRef != nil {
let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
println("There are \(loginItems.count) login items")
if(loginItems.count > 0)
{
let lastItemRef: LSSharedFileListItemRef = loginItems.lastObject as LSSharedFileListItemRef
for var i = 0; i < loginItems.count; ++i {
let currentItemRef: LSSharedFileListItemRef = loginItems.objectAtIndex(i) as LSSharedFileListItemRef
if LSSharedFileListItemResolve(currentItemRef, 0, itemUrl, nil) == noErr {
if let urlRef: NSURL = itemUrl.memory?.takeRetainedValue() {
println("URL Ref: \(urlRef.lastPathComponent)")
if urlRef.isEqual(appUrl) {
return (currentItemRef, lastItemRef)
}
}
}
else {
println("Unknown login application")
}
}
//The application was not found in the startup list
return (nil, lastItemRef)
}
else
{
let addatstart: LSSharedFileListItemRef = kLSSharedFileListItemBeforeFirst.takeRetainedValue()
return(nil,addatstart)
}
}
}
return (nil, nil)
}
Примечание только добавлено в оператор if после
let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
функция была длинной для комментария, и я думал, что это будет более полезно, чем несколько строк комментария
Примечание. Также необходимо было изменить возвращаемое значение для пустых списков
else
{
let addatstart: LSSharedFileListItemRef = kLSSharedFileListItemBeforeFirst.takeRetainedValue()
return(nil,addatstart)
}