Opening a Finder window

Hi there!

I downloaded an earlier version of XCode so I could complete the FileManager tutorial (it has errors with XCode 11/Swift 5.1) but I feel like I missed something because of the code already being written for us. I just…didn’t absorb what we were supposed to be doing as well as I do when going through making everything step by step.

Specifically, I’m just not sure I understand exactly what was used to open the finder window. I know it happens in here:

		// 1
		guard let window = view.window else { return }
		
		// 2
		let panel = NSOpenPanel()
		panel.canChooseFiles = false
		panel.canChooseDirectories = true
		panel.allowsMultipleSelection = false
		
		// 3
		panel.beginSheetModal(for: window) { (result) in
			if result.rawValue == NSFileHandlingPanelOKButton {
				// 4
				self.selectedFolder = panel.urls[0]
				print(self.selectedFolder)
			}
		}
  }

But I’m just not connecting how to replicate that in my own project.

For instance, this is what I have in a playground (modeled off the playground portion of the tutorial and also a discussion I was reading at Stack Overflow) which works:

import Cocoa

// Get the home directory url
let homePath = FileManager.default.currentDirectoryPath
let homeURL = URL(fileURLWithPath: homePath)
	.appendingPathComponent("Volumes")
	.appendingPathComponent("OneDrive")
	.appendingPathComponent("Audiobooks")

do {
	// Get the directory contents urls (including subfolders urls)
	let directoryContents = try FileManager.default.contentsOfDirectory(at: homeURL, includingPropertiesForKeys: nil)
	print(directoryContents)
	
	// if you want to filter the directory contents you can do like this:
	let mp3Files = directoryContents.filter{ $0.pathExtension == "mp3" }
	print("mp3 urls:",mp3Files)
	let mp3FileNames = mp3Files.map{ $0.deletingPathExtension().lastPathComponent }
	print("mp3 list:", mp3FileNames)

	let m4bFiles = directoryContents.filter{ $0.pathExtension == "m4b" }
	print("m4b urls:",m4bFiles)
	let m4bFileNames = m4bFiles.map{ $0.deletingPathExtension().lastPathComponent }
	print("m4b list:", m4bFileNames)

	let m4aFiles = directoryContents.filter{ $0.pathExtension == "m4a" }
	print("m4a urls:",m4aFiles)
	let m4aFileNames = m4aFiles.map{ $0.deletingPathExtension().lastPathComponent }
	print("m4a list:", m4aFileNames)

	let aaxFiles = directoryContents.filter{ $0.pathExtension == "aax" }
	print("aax urls:",aaxFiles)
	let aaxFileNames = aaxFiles.map{ $0.deletingPathExtension().lastPathComponent }
	print("aax list:", aaxFileNames)
	
} catch {
	print(error)
}

(I’m sure there must be a much less repetitive way to structure the filters, but I haven’t gotten far enough to research it yet)

But I’m just…not making the connection on how to open a finder window with those filters.

I’m so close, but this is just a little beyond what I understand how to do at this point in my learning process. What am I missing?

@sarah Can you please help with this when you get a chance? Thank you - much appreciated! :]

Hi I actually did eventually figure this out, so it’s all good. Thank you!

What I have is…

@IBAction func inputBrowseClicked(_ sender: Any) {
    let inputPanel = NSOpenPanel()
    inputPanel.canChooseFiles = true
    inputPanel.canChooseDirectories = false
    inputPanel.allowsMultipleSelection = false
    inputPanel.allowedFileTypes = ["mp3","aax","m4b","m4a"]
    let userChoice = inputPanel.runModal()
    switch userChoice{
    case .OK :
        if let inputFileChosen = inputPanel.url {
           (some stuff)
        }
    case .cancel :
        print("user cancelled")
    default:
        break
    }
}

Hi,

As you have worked out, NSOpenPanel & NSSavePanel open file dialogs - not really Finder windows, but they open the dialogs that allow you to select files for opening or saving. And you have worked out how to limit the selections to certain file types.

Your code for filtering out various file types from a selected folder is fine, but if you need to allow the user to select a folder and then list all the matching files in it, you might want to use a directory enumerator instead of contentsOfDirectory.

You could try something like this:

    let fileManager = FileManager.default
    let validFileExtensions = ["mp3", "aax", "m4b", "m4a"]
    
    let dirEnum = fileManager.enumerator(at: folder,
                                         includingPropertiesForKeys: [],
                                         options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants])

    while let url = dirEnum?.nextObject() as? URL {
        if validFileExtensions.contains(url.pathExtension) {
            print(url.lastPathComponent)
        }
    }

And one last thing… if you want to open a new Finder window at a folder, you can use this:

NSWorkspace.shared.open(selectedFolderUrl)

I hope this answers your questions, but please get back to me if there is anything that is still unclear.

Sarah