こんにちは、新卒1年目の福成です。
大半のデスクトップアプリでは、保存先を指定したり別名で保存する機能がありますが、
その際には保存先のパスの取得が欠かせません。


今回は、以下の機能をクロージャを用いて実装をしました。
- 実行するとFinderが開かれる
- 開かれたFinderでフォルダを選択すると、そのフォルダのパスを取得する
開発環境
- Xcode 8.1
- Swift 3.0.1
関数の定義
NSOpenPanel
基本的にNSOpenPanelが肝となっています。 このPanelというのがおそらく我々がよく目にするFinderのことを表しているのではないかと思います。 各プロパティではファイルやフォルダの選択ができるかどうかや複数選択を許すかどうかを Booleanで決めることができます。
クロージャ
openPanel.beginはFinderを開いてフォルダを選択する流れとなっていますが、 これが非同期処理のために、普通の関数で実装すると、 フォルダを選択してパスを取得する前に関数内の処理が終わってしまいます。。
そのため、selectFolderPathはクロージャを引数で受け取る関数として実装しています。
@escaping
スコープをまたいでクロージャを参照したいため、@escapingを引数に用いています。
ソースコード
import Cocoa
class OpenPanel: NSObject {
/**
Finderを開いてフォルダのパスを取得する
- parameter callback: フォルダのパス
*/
static func selectFolderPath(callback: @escaping (String?) -> Void) {
let openPanel: NSOpenPanel = NSOpenPanel()
openPanel.canChooseFiles = false
openPanel.canChooseDirectories = true
openPanel.allowsMultipleSelection = false
let selectDestinationFolder: String = "保存先を指定"
openPanel.message = selectDestinationFolder
openPanel.begin(completionHandler: { (result) -> Void in
switch result {
// フォルダを選択し、openを押下したとき
case NSFileHandlingPanelOKButton:
callback(URLtoString(path: openPanel.url))
break
// cancelを押下したとき
case NSModalResponseCancel:
callback(nil)
break
default:
return
}
})
}
/**
URLをStringに変換する
- parameter path: URL
- returns: String型に変換されたURL
*/
static private func URLtoString(path: URL?) -> String? {
guard let path = path else {
return nil
}
let pathAbsoluteString: String = path.absoluteString
return pathAbsoluteString.removingPercentEncoding
}
}
呼び出し側
フォルダを選択したタイミングでコールバックが返され、 下記ソースコードの3行目以降の処理が実行されるようになっています。 また、Cancelボタンを押下するとnilを返すようにしているので、 フォルダーを選択したときのみ処理が継続するようにguard文を用いています。
ソースコード
OpenPanel.selectFolderPath() { directoryPathAbsoluteStringDecoded in
guard let directoryPathAbsoluteStringDecoded: String = directoryPathAbsoluteStringDecoded else {
return
}
// フォルダーを選択したときの処理を記述する
print(directoryPathAbsoluteStringDecoded)
}
実行結果
フォルダを選択すると、そのパスを取得ができていることができていることがわかります。 また、選択画面でCancelボタンを押すとnilを返すようにしています。


感想
NSOpenPanelの部分は難しくなく、関数化せずにべた書きするだけならわりとすぐに実装が終わりました。
が、クロージャーの意味や使い方にだいぶ苦しみました。。 とくにSwiftにおけるクロージャーはたくさん使い方があるらしく、 使い所に合わせて都度勉強が必要だなと感じました。
参考
福成毅
2年目のiOSエンジニアです!