Swift - 让Mac App在启动时启动

sim*_*per 17 macos cocoa swift

在过去的几周里,我一直在swift编写一个mac应用程序,既可以进入mac编程,也可以在我的工作场所迁移到它时快速练习.我正在尝试通过定制在BDungan博客上提供的代码来获取一些代码来将我的应用程序添加为"启动时启动"应用程序

到目前为止,在搞乱了几个小时后,我想出了以下内容:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,我遇到了两个问题.

问题1

Swift不希望我将NSURL与CFURLRef进行比较......现在我提出了一个Xcode建议只是为了让应用程序运行,但我100%确定它没有按照我的想法行事.(参见//问题1).

在objective-c中,似乎在NSURL和CFURLRef(或CFURL)之间允许免费桥接,但是在尝试强制转换,条件转换或任何种类的swift(在此处插入正确的字符)接近我的代码时,不可避免地会构建.我得到的错误如下:

Unmanaged不是NSURL的子类型: if appURL as Unmanaged<CFURLRef> == itemURL

等等

问题2

虽然这段代码目前没有给出任何警告或错误......但是当我尝试编译时,我得到了一个Command failed due to signal: Segmentation fault: 11坦率的...超出了我.

小智 12

我已经设法根据Brian Dunagan的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")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


nsi*_*j22 5

惊人的答案,但你忘了一个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)
}
Run Code Online (Sandbox Code Playgroud)

注意刚刚添加到if语句之后

let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
Run Code Online (Sandbox Code Playgroud)

该功能是为了征求意见,我认为这比评论的几行更有帮助

注意:还必须更改空列表的返回值

else
{
    let addatstart: LSSharedFileListItemRef = kLSSharedFileListItemBeforeFirst.takeRetainedValue()
    return(nil,addatstart)
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,在OS X 10.10中,不推荐使用`LSSharedFileListItemResolve`.我不得不直接比较`LSSharedFileListItemCopyResolvedURL(currentItemRef,0,nil).takeRetainedValue()`到`currentItemUrl`来实现这个功能(在遵循XCode的其他建议之后). (4认同)
  • 看起来我的代码是`let currentItemUrl = LSSharedFileListItemCopyResolvedURL(currentItemRef,0,nil).takeRetainedValue()`但我相信这在某些情况下给了我错误.我的hacky解决方法:https://github.com/ComputeForHumanity/compute-for-humanity-app/commit/bd7369d4b07114a7eb7081d37e6a9bf025f5bb3f (2认同)