Cocoa
macos
ATS
Xcode8
Xcode9

macOS で ネットからURLでダウンロードするコードが動かなくなり焦って調べてみた

More than 1 year has passed since last update.

macOS のアプリのコードでネットからのコンテンツがダウンロードされない!?

macOS の cocoaアプリの新規プロジェクトを作り、こんなコードを書いたら動かない事に気がつきました。

do {
    let url = URL(string: "https://www.apple.com")!
    var encoding = String.Encoding.utf8
    let text = try String(contentsOf: url, usedEncoding: &encoding)
    print("contents: \(text)")
}
catch {
    print("error: \(error)")
}

エラーのログはだいたいこんな感じです。

error.log
dnssd_clientstub ConnectToServer: connect()-> No of tries: 1
dnssd_clientstub ConnectToServer: connect()-> No of tries: 2
dnssd_clientstub ConnectToServer: connect()-> No of tries: 3
dnssd_clientstub ConnectToServer: connect() failed path:/var/run/mDNSResponder Socket:11 Err:-1 Errno:1 Operation not permitted
[] nw_resolver_create_dns_service_locked DNSServiceCreateDelegateConnection failed: ServiceNotRunning(-65563)
TIC TCP Conn Failed [1:0x604000162040]: 10:-72000 Err(-65563)
Task <7E9A6646-3924-4A82-868D-DA280CA5ECFC>.<0> HTTP load failed (error code: -1003 [10:-72000])
NSURLConnection finished with error - code -1003
Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist." UserInfo={NSURL=https://www.apple.com, NSUnderlyingError=0x6000000500e0 {Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo={NSUnderlyingError=0x608000440ed0 {Error Domain=kCFErrorDomainCFNetwork Code=-1003 "A server with the specified hostname could not be found." UserInfo={NSErrorFailingURLStringKey=https://www.apple.com/, NSErrorFailingURLKey=https://www.apple.com/, _kCFStreamErrorCodeKey=-72000, _kCFStreamErrorDomainKey=10, NSLocalizedDescription=A server with the specified hostname could not be found.}}, NSErrorFailingURLStringKey=https://www.apple.com, NSErrorFailingURLKey=https://www.apple.com, _kCFStreamErrorDomainKey=10, _kCFStreamErrorCodeKey=-72000, NSLocalizedDescription=A server with the specified hostname could not be found.}}}

ちょっと前まで書いていた似たようなコードではちゃんと動いていました。ここで、ひらめきます。「あ!これはあれだ!App Transportation Securityの問題かな?」とそこで、info.plist を開いてこんな感じで App Transportation Security を追加して Allow Arbitrary LoadsYES にします。

Screen Shot 2017-10-02 at 23.35.28.png

実行するとやはり、同じエラーで、コンテンツをダウンロードしてくれません。そこで、エラーの一部からググってみますが、問題の解決にたどり着けそうにありません。

で、プロジェクトをよく見るとこれまでに見慣れないものが入っている事に気がつきます。「.entitlements」です。「はは〜ん!Xcode 9で新規に作ったcocoaのアプリのプロジェクトにはデフォルトで入るのだな!」そこで、Xcode8 と Xcode9 でそれぞれ新しくcocoaのアプリのプロジェクトを作ってみます。左が Xcode8 で右が Xcode9 です。

screen_shot_1.png screen_shot_1.png

やっぱり、Xcode 9で作ったプロジェクトには「.entitlements」がふえています。

Screen Shot 2017-10-03 at 0.02.26.png

そこで com.apple.security.app-sandboxcom.apple.security.files.user-selected.read-only で検索してみます。

Enabling App Sandbox

https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html

なるほど、Cocoa アプリ が Sandbox化 されるなら、各々の機能別にこれで指定しないとダメだよね。という事で、一覧を眺めて com.apple.security.network.client を Xcode9 で作成されたプロジェクトに入れてみました。

Screen Shot 2017-10-02 at 23.39.08.png

これで、再度冒頭のスニペットを組み込んで実行させると、見事ネットからURLでコンテンツをロードしてくれました。

なお、エラーメッセージで検索しても、解決できるページにたどりつかなかった為、今後、同様な問題を抱えた人がエラーメッセージを頼りに検索したした時に、ここにだどりつけるように、エラーメッセージなどもほぼそのまま掲載する事とします。

最後に

あと、既に気がついている人はいるかもしれませんが、冒頭のスニペットが動かないので App Transportation SecurityAllow Arbitrary LoadsYES すればいいと思いましたが、そもそも https:// でアクセスしてるのでこの思考の流れは残念でした。

また、macOS の Sandbox化が進むとユーザーには良いかもしれませんが、開発者には面倒な時代になっていくなぁという思いが交差しています。

環境に関する表記

Xcode 8: Xcode 8.3.3 (8E3004b)
Xcode 9: Xcode 9 (9A235)