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)
}