【Swift】URLSession メソッドの使い分け

以前、通信部分の書き方を調べていたときに、

👉 【Swift】URLSession.shared.dataTask() をうまく使いこなせない hatena-bookmark

古い API の公での dis がワロエタ。

👉 Use async/await with URLSession - WWDC21 - Videos - Apple Developer hatena-bookmark

先人を敬えよ。

でも分かりやすくなって非常に良い。

 

🧑🏻‍💻 新しいAPIの使い分け

新しい API として以下が上げられている。


URLSession.shared.data() 
URLSession.shared.download()
URLSession.shared.upload()
URLSession.shared.bytes()

ドキュメントを見ながら、引数と戻り値がわかりやすように並べてみる。


func data(
    for request: URLRequest,
    delegate: (any URLSessionTaskDelegate)? = nil
) async throws -> (Data, URLResponse)


func data(
    from url: URL,
    delegate: (any URLSessionTaskDelegate)? = nil
) async throws -> (Data, URLResponse)


func download(
    for request: URLRequest,
    delegate: (any URLSessionTaskDelegate)? = nil
) async throws -> (URL, URLResponse)


func download(
    from url: URL,
    delegate: (any URLSessionTaskDelegate)? = nil
) async throws -> (URL, URLResponse)


func download(
    resumeFrom resumeData: Data,
    delegate: (any URLSessionTaskDelegate)? = nil
) async throws -> (URL, URLResponse)


func upload(
    for request: URLRequest,
    from bodyData: Data,
    delegate: (any URLSessionTaskDelegate)? = nil
) async throws -> (Data, URLResponse)


func upload(
    for request: URLRequest,
    fromFile fileURL: URL,
    delegate: (any URLSessionTaskDelegate)? = nil
) async throws -> (Data, URLResponse)


func bytes(
    for request: URLRequest,
    delegate: (any URLSessionTaskDelegate)? = nil
) async throws -> (URLSession.AsyncBytes, URLResponse)


func bytes(
    from url: URL,
    delegate: (any URLSessionTaskDelegate)? = nil
) async throws -> (URLSession.AsyncBytes, URLResponse)

なんとなく見えてきます。

 

🧑🏻‍💻 使い分けの基準

使い分けの基準としては、

「受け取りたいデータ形式」

ですね。

受け取ってからの利用するデータ形式が、


テキスト → .data()

ファイル → .download()

バイトバッファー → .bytes()

となります。

あとは、その他として


ファイルを送りたい → .upload()

となります。

リクエスト後のレスポンスは、すべてにある戻り値 URLResponse を使って


urlresponse as? HTTPURLResponse

のようにダウンキャストして、レスポンスコードなどを確認します。

 

🧑🏻‍💻 URL か URLRequest か

それぞれのメソッドに引数には、URL か URLRequest があります。

これらの使い分けとしては、

リクエストメソッド・ヘッダーのカスタムが


必要でない → URL

必要 → URLRequest

というかんじでしょうか。

リクエストヘッダーのカスタムは以下のような感じで書けるようです。


var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer 123456ABC", forHTTPHeaderField: "Authorization")
request.httpBody = try! JSONEncoder().encode(User(name: "John", age: 15))

let (data, response) = try await session.data(for: request)
print((response as! HTTPURLResponse).statusCode)
print(String(data: data, encoding: .utf8)!)

 

🧑🏻‍💻 まとめ

「リクエストヘッダーを常に意識しながら、JSONを受け取る。」

という感じの以下が最も使える基本的なメソッドとなるように思います。


func data(
    for request: URLRequest,
    delegate: (any URLSessionTaskDelegate)? = nil
) async throws -> (Data, URLResponse)

このメソッドだけで、

リクエストメソッドとヘッダー次第で、

ほとんどできるのではないかと思ったりもする。

👉 【Swift】ファイルやディレクトリのパスが長すぎていやだ – URL.shortPath() hatena-bookmark

最後に、便利な無料確認サービスをどうぞ。


👉 httpbin.org hatena-bookmark


【Apple】歴代 Apple WWDC やイベントの動画を一括にダウンロードする方法 WWDC24 も追加更新中

いわゆる ffmpeg を使った シェルスクリプトです。

以下のようなものです。


ffmpeg -i https://devstreaming-cdn.apple.com/videos/wwdc/2020/10691/2/A92788CB-81ED-4CCF-B6B1-4DD7A1F3E87D/hvc_2160p_16800/prog_index.m3u8 -c copy "Session - 10691 temp.mp4"
ffmpeg -i https://devstreaming-cdn.apple.com/videos/wwdc/2020/10691/2/A92788CB-81ED-4CCF-B6B1-4DD7A1F3E87D/audio_english_192/prog_index.m3u8 -c copy "Session - 10691 temp.aac"
ffmpeg -i "Session - 10691 temp.mp4" -i "Session - 10691 temp.aac" -c copy "Session 10691 - Monday@WWDC.mp4"
rm "Session - 10691 temp.mp4"
rm "Session - 10691 temp.aac"

しかし、なぜか QuickPlayer で映像が見えず音声だけしか再生されない。

VLCなど別の動画プレーヤーではフツーに見れます。

現在開催中の WWDC24 の動画分も更新中の様子。

まとめてみたい人は便利かもしれません。

👉 dmthomas/AppleVideoDownloadScripts: Script to download higher resolutions of Apple event videos using ffmpeg hatena-bookmark


【Swift】 文字コードの変換 を整理してみた

前にやってみた、これ。


2つの型(プロトコル)の間を相互に変換するだけでも、いろんな書き方があるのだな。

などと思いつつ気になってたのが文字コードの変換。

👉 文字コード - Wikipedia hatena-bookmark

今回、おおまかに整理しておきたい。

 

🧑🏻‍💻 String の内部エンコーディングは UTF-8

Swift 5 switches the preferred encoding of strings from UTF-16 to UTF-8 while preserving efficient Objective-C-interoperability.

👉 Swift.org - UTF-8 String hatena-bookmark

String 内部のエンコーディングは「UTF-8」になったらしいです。

 

🧑🏻‍💻 String、 Data とファイル間での相互変換

図で。大体こんな。

まずは、エンコーディングの引数名が、encoding:, using:, as: と多様なので初見、分かりづらかった。


「String から」または「Stringへ」変換する場合に

「相手」 (「変換先」または「変換元」) のエンコーディングを指定する。

String 自体は「UTF-8」として意識。

Data と ファイル間の変換にはエンコーディングの指定は不要。

Data はそれぞれに変換済みのバイトシーケンス。

 

🧑🏻‍💻 変換してみる

コードを書いて試してみます。

👉 【Swift】よく使いそうな String の format hatena-bookmark


"あいうえお"
  .data(using: .shiftJIS)!.map { String(format: "%02X", $0) }.joined()
// 10 bytes 82A082A282A482A682A8

からスタートして一通り。

以前書いた extension を使っています。

👉 【Swift】ファイルやディレクトリ操作するための extension をまずは作った hatena-bookmark

これで大体いけるはず。

注意としては、「変換できない文字もある」ということ。


o  Shift_JIS → UTF-8 
x  UTF-8     → Shift_JIS

対応する文字がない。

 

🧑🏻‍💻 参考

👉 String.Encoding | Apple Developer Documentation hatena-bookmark
👉 shiftJIS | Apple Developer Documentation hatena-bookmark
👉 init(data:encoding:) | Apple Developer Documentation hatena-bookmark
👉 init(contentsOf:encoding:) | Apple Developer Documentation hatena-bookmark
👉 data(using:allowLossyConversion:) | Apple Developer Documentation hatena-bookmark
👉 write(to:atomically:encoding:) | Apple Developer Documentation hatena-bookmark
👉 init(contentsOf:options:) | Apple Developer Documentation hatena-bookmark
👉 write(to:options:) | Apple Developer Documentation hatena-bookmark