3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Swift】Vapor で URLSession を使用したいときの注意点

Last updated at Posted at 2023-05-05

こんにちは。kamimiです。🌞

Vapor で API を実装中です。ようやく Google Cloud Run(以下GCR) にデプロイする段階にきて、イメージをビルドしたときにとあるエラーが発生しました。

ローカルでビルドしている時は気づかなかったので、今後の自分や他の人のために記事に残しておこうと思います。

発生したエラー

ようやく API を実装し終わったので、GCR にデプロイしようと思い、以下のコマンドを実行しました。

gcloud builds submit --tag gcr.io/iosappreviewstatuschecker/checker --timeout=20m

その時に以下のようなエラーが発生しました。
「この型は、FoundationNetworking モジュールに移動しました。使用するには、そのモジュールをインポートしてください。」

/build/Sources/App/Infrastructure/API/Session.swift:19:26: error: 'URLSession' is unavailable: This type has moved to the FoundationNetworking module. Import that module to use it.
    private let session: URLSession
                         ^~~~~~~~~~

対処方法

調べたところ、そもそも本当にURLSessionを使う必要があるのか?を考えた方が良さそうです。理由は後ほど記述します。その上で使いたい場合は後述の処理をしましょう。

本当に URLSession を使う必要があるか?

私の場合、最終的に URLSession を使用しないという選択肢をとりました。機能を削ったわけではなく、別の方法で実装しました。

みなさんもこの代替方法が適用可能かを考えた上で、URLSessionを使用するべきか考えてみてください。

結論、以下の理由から私は使用しませんでした。

  1. そもそも URLSession を使用しなくても済むように、Vapor が API を提供しており、それで事足りた
  2. Vapor では Linux のための Foundation の非同期 API が使用できない(dataメソッドなどの基本的な API が使用できない)
  3. デプロイ時のイメージサイズが大きくなる

理由1. そもそも URLSession を使用しなくても済むように、Vapor が API を提供しており、それで事足りた

私の場合、URLSessionを使用したい理由は、外部の API を呼びたいからでした。後述する「やっぱり URLSession を使いたい場合」の章に記載した通りに対処すれば、イメージビルド時にエラーは発生しなくなりました。

ただドキュメントを見ていたところ、URLSession を使用しなくても済むように、Vapor が API を提供してくれていることがわかりました。

サーバーサイドの実装に疎いせいか、iOS アプリの実装と同じ感覚で実装していたせいか、 Vapor がそういった API を用意していることに気づかないまま、 いつもの感覚で URLSession を使用していました。

ちなみに以下のように使用することができます。(ドキュメントからそのまま引用)

let response = try await req.client.post("https://httpbin.org/status/200") { req in
    // Encode query string to the request URL.
    try req.query.encode(["q": "test"])

    // Encode JSON to the request body.
    try req.content.encode(["hello": "world"])

    // Add auth header to the request
    let auth = BasicAuthorization(username: "something", password: "somethingelse")
    req.headers.basicAuthorization = auth
}
// Handle the response.

例えば自分で API のレスポンスをもっとカスタマイズしたい場合などは、カスタマイズの仕方によってこの API は使用しないという判断になる可能性もあると思います。

カスタマイズ性については調べていないので詳しくはわかりませんが、Vapor が便利な API を提供してくれているので、この API を使用した方がコードが複雑にならず可読性が上がって良いのではと思いました。

理由2. Vapor では Linux のための Foundation の非同期 API が使用できない

Vapor では Linux のための Foundation の非同期 API が使用できません。 ローカルでビルドして localhost で動作確認しているときには問題なく動くので、気づかなかったです。。😇

実際、私は以下のように実装していたのですが、イメージのビルド時にエラーになってしまいました・・・

let (data, response) = try await URLSession.shared.data(for: urlRequest)

以下の stack overflow で Linux のための Foundation の非同期 API が使用できないことを知りました。回答者が Vapor Core Team のお一人である 0xTim さんなので、かなり信用度の高い情報だと思っています。

The async APIs for Foundation on Linux aren't available yet (they're different from the actual language concurrency features).
For a Vapor app you shouldn't really be using URLSession to make requests, it doesn't fit with how the framework works. Use Vapor's client instead (which has async APIs)

回答にも、Vapor のクライアント API を代わりに使用してください、とありますね。
フレームワークの動作に合わないから、とのこと。

理由3. デプロイ時のイメージサイズが大きくなる

どうしても URLSession を使用したい場合、libcurl4 というライブラリをインストールする必要があります。ですがそうするとイメージのサイズが大きくなってしまいます。

どのくらいサイズが大きくなるのかは、以下の Issue コメントで調べてくれた人がいました。🙏🏻

イメージが大きくなると、デプロイする時に時間がかかったり、ストレージを消費してしまったりするので、小さくできるならばした方が良いようです。

理由1 でも書いたように、Vapor が API を提供してくれているので、それを使えば良いかと思います。

やっぱり URLSession を使いたい場合

それでも URLSession を使いたい場合、以下を実施すればビルドが成功するようになります。

  1. libcurl4 をインストール
  2. FoundationNetworking をインポートする

手順1. libcurl4 をインストール

Dockerfileの RUN にあるlibcurl4のコメントアウトを外します。
これでうまく動くようになります。簡単ですね。

Dockerfile
# Make sure all system packages are up to date, and install only essential packages.
RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \
    && apt-get -q update \
    && apt-get -q dist-upgrade -y \
    && apt-get -q install -y \
      ca-certificates \
      tzdata \
# If your app or its dependencies import FoundationNetworking, also install `libcurl4`.
      libcurl4 \  # ここのコメントアウトを外す
# If your app or its dependencies import FoundationXML, also install `libxml2`.
      # libxml2 \
    && rm -r /var/lib/apt/lists/*

コメントに FoundationNetworking をインポートする場合は libcurl4 をインストールしてね、と書いており親切だなと思いました。

この親切なコメントが入ったのは以下の Issue がきっかけのようです。感謝。

手順2. FoundationNetworking をインポートする

次に FoundationNetworking をインポートする必要があります。

URLSession を使用するソースコードのファイルで、以下のように書きます。
Linux では URLSessionFoundationNetworking というモジュールに存在しているためです。

#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

おわりに

URLSession を使うのは避けるべきだということに気づいたのが、本番デプロイをするときになってしまったために、書き直すハメになってしまいました。。。が勉強にはなりましたし、この記事を書くきっかけになったので結果オーライだと思うことにしました!笑

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?