同期処理と非同期処理
APIと通信を行なって値を得る際には、処理を同期的に行うか、非同期で行うかという方法の違いがあります。ここでは、同期処理と非同期処理の違いと実際にAPIから値を得る具体的な処理つについて説明します。
APIと通信する
前回のサンプルの続きから説明します。
struct ContentView: View { // Pixabay API key let api_key = "xxxxxxxxxxxxxx" var body: some View { VStack { Button("Button") { Task { do { let urlStr = "https://pixabay.com/api/?key=\(api_key)&q=ねこ" let url = URL(string: urlStr.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!)! let (data, response) = try await URLSession.shared.data(from: url) print(response) print(data) } catch { print("エラー発生") print(error) } } } Text("Hello, world!") .padding() } } }
APIとHTTP通信を行うときには、URLSession構造体を利用します。
https://developer.apple.com/documentation/foundation/urlsession
URLSessionは、インスタンスを生成せずにsharedプロパティでオブジェクトを参照して利用します。
ドキュメントを参照すると、HTTP通信を行なってデータを取得する処理にはいくつものメソッドがあります。その各メソッドには、同期的に実行する同期処理と非同期で実行する非同期処理の2つの実行の方法があります。先に同期処理と非同期処理について説明します。
同期処理と非同期処理の違い
Swiftの本来の処理は非同期処理です。サンプルのようにAPIから値を得るというちょっと時間のかかる処理に関しては、メソッドの実行の際にデリゲートという仕組みを利用して、メソッドが実行された結果は別のオブジェクトに渡す、という仕様でした。
つまりメソッドの実行と結果の取得がコード内で離れた箇所で行われていました。
これに対して、同期処理とはメソッドの実行と結果の取得を同時に行う、というものです。
両者の違いは次の図のイメージです。
同期処理においては、時間がかかる処理の結果が出るまで待機しなければなりません。この待機状態をつくる、という処理がコード内の「await」です。
let (data, response) = try await URLSession.shared.data(from: url)
サンプルでは、URLSession構造体の data メソッドの結果を await を記述して直接取得しています。
Apple のドキュメントでの書式も改めて確認しておきます。
https://developer.apple.com/documentation/foundation/urlsession/3767352-data
メソッドの定義部分を参照すると、delegate引数はオプショナル型で定義されているので書かなくても構いません。
同期処理での実行が可能なメソッドは上記のように「async」が書式に記載されています。
サンプルでは、dataメソッドを await で同期的に実行し、メソッドの実行結果である「( Data, URLResponse )」を変数「( data, response )」で受け取るように書いています。
let (data, response) = try await URLSession.shared.data(from: url)
メソッドの実行結果のオブジェクトのことを「返り値」や「戻り値」と呼びます。dataメソッドの戻り値は、DataオブジェクトとURLResponseオブジェクトです。2つのオブジェクトを「( )」内に並べて記述することを「タプル」と呼びます。
dataメソッドを利用するときには、try、 do-catch 文、await、タプルと複数の仕様が関わります。
最初戸惑うかもしれませんが、迷ったらドキュメントを参照するなどして確実に意味を理解するようにしてください。
APIから取得した値を確認する
APIから値を取得する処理の流れがわかったところで、実際にPixabay API から得られたJSONを確認してみましょう。URLSession構造体のdataメソッドでは、DataオブジェクトとURLResponseオブジェクトが得られます。このうちのDataオブジェクトにJSONが格納されています。URLResponseオブジェクトに関しては10章で説明します。
DataオブジェクトはSwiftで扱う汎用的なデータのオブジェクトです。ここからJSONを取得できるようにString型のオブジェクトに変換します。Swiftでは日本語や中国語といったマルチバイトの文字を扱う場合は、UTF-8の文字コードで扱います。DataオブジェクトからString型のオブジェクトに変換する処理は、String構造体のinitメソッドで行います。
https://developer.apple.com/documentation/swift/string/3126741-init
init?(data: Dataオブジェクト, encoding: String.Encodingオブジェクト)
String.Encodingオブジェクトは文字コードを指定するオブジェクトです。UTF-8を指定する場合は「String.Encoding.utf8」です。引数の型がわかっているので「.utf8」と記載することもできます。
サンプルに次のコードを追記してください。
do {
let urlStr = "https://pixabay.com/api/?key=\(api_key)&q=ねこ"
let url = URL(string: urlStr.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!)!
let (data, response) = try await URLSession.shared.data(from: url)
print(response)
print(data)
print(String(data: data, encoding: .utf8)!)
} catch {
print("エラー発生")
print(error)
}
String構造体のinitメソッドはオプショナル型に対応しており、サンプルでは引数のDataオブジェクトが存在しているので、メソッドの実行時には「!」をつけています。
シミュレーターを起動してアプリを実行した後で、ボタンを押すとコンソールにPixabay API から得られたJSONの文字列が表示されます。
改行がなされないので分かりにくいですが、ブラウザでも同じURLにアクセスして同じ結果が出ていることを確認してください。
APIからの値の取得には利用する処理が複数にわたりますので、前回と今回の2回で説明しました。
次回はAPIから得られたJSONを解析する方法について説明します。