Swift4 任意の場所の外部ファイルの読み込み

Swift4になってCocoa app(mac app)で外部ファイル(txt)の読み込みができず、Swift/Xcodeの頻繁な仕様変更のせいか解決策が見つからずなかなかに苦労しました。

結局、ファイル選択ダイアログを実装することで解決しました。

f:id:simonsnote:20171012202927j:plain

経緯

Playgroundだとうまくいくのに

Google検索を参考にこんなコードを書いてみてPlaygroundで試したらうまくいきました。

let importURL:URL = URL(fileURLWithPath: "/Users/ユーザー名/Desktop/~~~.txt")
do {
    str = try String( contentsOf: importURL, encoding: String.Encoding.utf8 )
    print(str)
} catch {
    print(error)
}

しかしいざ実装しようとしたところ、なぜかエラー...
エラーメッセージを見ると、
Error Domain=NSCocoaErrorDomain Code=257
"The file “~~~.txt” couldn’t be opened because you don’t have permission to view it."

ふむ、アクセス権の問題...?

どうやらSandboxのせい

どうやらセキュリティのため、Mac appからアクセスできる先はずいぶんと制限されてるもよう。
qiita.com
簡単にいえば、アプリ内の専用フォルダ以外へのアクセスには都度ユーザーの許可が必要とのこと。だから上で書いたコードでアクセスできる先は/Users/ユーザー名/Library/Containers/Bundle Identifierの名前/Data内だけだったんです。そこにファイルをいれて試したらうまくいきました。(ちなみにこの専用フォルダの場所はprint(NSHomeDirectory())を実行してみるとわかります。)

好きな場所から選びたい

しかし読み込むファイルをあらかじめ専用フォルダにコピーしておかなきゃいけないなんて全く実用的じゃない。
これを回避するには、パスを文字列の形で入力するプリミティブな方式でなく、いわゆる「ファイル選択ダイアログ」を出してユーザーに選択させればいいのです。

ということで、これらの記事を参考にダイアログを実装してみました。
qiita.com
www.kilinbox.net

完成したコード

ということで、できたコードがこちら。

let openFileDialog = NSOpenPanel()
openFileDialog.canChooseDirectories = false
openFileDialog.canChooseFiles = true
openFileDialog.canCreateDirectories = false
openFileDialog.allowsMultipleSelection = false
if openFileDialog.runModal().rawValue == NSApplication.ModalResponse.OK.rawValue {
    if let fileURL = openFileDialog.url { //fileURLはユーザーが指定(=合法的に取得)したパスなので、開ける。
        do {
            let fileContent:String = try String( contentsOf: fileURL, encoding: String.Encoding.utf8 )
            print(fileContent) //読み込んだ内容
        } catch {
            print(error)
        }
    }
}

今回はテキストファイル(.txt)を読み込む前提ですが、合法なパスがゲットできているので、他の形式で読み込む場合はdo{}の中身を変えればOK。

疑問点などあればコメントにどうぞ!