4

I'm working on a macOS cocoa-app in Swift where I import several different file types into the app for the user to interact with.

I'm currently trying to determine if it's possible to implement the "Open file with" feature, so that the user could open those files in a different program if they wanted to:

enter image description here

I've found a few different SO questions that seem tangentially related to what I'm trying to do:

Swift: How to open file with associated application?

Launch OSX Finder window with specific files selected

...but so far nothing to indicate if it's possible to implement right-click Finder/file (?) access in the way I had in mind.

Apologies if this is too vague of a question; any help / guidance appreciated!

1
  • What does your application have to do with Finder's Open With? Commented Mar 2, 2018 at 23:28

3 Answers 3

3

Without going into details, it's pretty straight forward:

  1. Get the list of all known applications that can open a specific file type (see LSCopyApplicationURLsForURL, a Core Foundation C function).
  2. Build the menu. You can use NSWorkspace (and probably URL) to get the application icons.
  3. Use NSWorkspace.openFile(_:withApplication:) to tell the application to open the given document.
Sign up to request clarification or add additional context in comments.

Comments

3

LSCopyApplicationURLsForURL is deprecated. You can use this alternative:

func getListOfExternalApps(forURL url: URL) -> [(URL, Image)] {
    let listOfExternalApps = NSWorkspace.shared.urlsForApplications(toOpen: url)
            
    let icons = listOfExternalApps.map {
        let nsimage = NSWorkspace.shared.icon(forFile: $0.path())
        nsimage.size = CGSize(width: .s16, height: .s16)
        return Image(nsImage: nsimage)
    }

    return Array(zip(listOfExternalApps, icons))
}

Comments

2

2022, Swift 5

Get app list associated with local file:

func getAppsAssociatedWith(_ url: URL?) {
    guard let url = localFileURL,
          let retainedArr = LSCopyApplicationURLsForURL( url as CFURL, .all)?.takeRetainedValue(),
          let listOfRelatedApps = retainedArr as? Array<URL>
    else {
        return []
    }
        
    return listOfRelatedApps
}

Getting thumbnail for app:

let singleAppIcon = NSWorkspace.shared
                       .icon(forFile: appUrl.path)
                       .scaledCopy(sizeOfLargerSide: 17)

Open url with app:

@available(macOS 10.15, iOS 9.0, *)
public class func openUrlWithApp(_ urls: [URL], appUrl: URL) {
    NSWorkspace.shared.open(urls, withApplicationAt: appUrl, configuration: NSWorkspace.OpenConfiguration())
}

In my app I'm cashing all apps icons in dictionary.

[someFile localURL : app icon]

If I have already got icon earlier - no need to get it once more

var relatedAppsThumbnails: [URL: Image] = [:]

func updateRelatedApps() {
        guard let url = currImgUrl, // file url to get icons from related apps
              let retainedArr = LSCopyApplicationURLsForURL( url as CFURL, .all)?.takeRetainedValue(),
              let listOfRelatedApps = retainedArr as? Array<URL>
        else {
            relatedApps = []
            return
        }
        
        self.relatedApps = listOfRelatedApps
        
        // add app icon in case of it wasn't added yet
        for appUrl in listOfRelatedApps {
            if relatedAppsThumbnails[appUrl] == nil {
                let nsImg = NSWorkspace.shared.icon(forFile: appUrl.path)
                    .scaledCopy(sizeOfLargerSide: 17)
                
                relatedAppsThumbnails[appUrl] = Image(nsImage: nsImg)
            }
        }
    }

1 Comment

LSCopyApplicationURLsForUR is deprecated. I've added an alternative answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.