はじめに
先日Appleから Swift HTTP Types が公開されました。
せっかくなので、APIコール処理を標準と書き比べてみます。
環境
- OS:macOS Ventura 13.4.1
- Swift:5.8
- swift-http-types:0.2.1
例
前回の記事と同様、APIからディズニーランドの住所を取得して構造体に格納します。
http://zipcloud.ibsnet.co.jp/api/search?zipcode=2790031
実装を比べてみる
標準とswift-http-typesで実装を比べてみます。
共通処理
両方で使う共通の処理です。
構造体
取得する住所を格納する構造体です。
前回の記事 の構造体をほぼそのまま流用します。
struct Address: Decodable {
var results: [Result]
struct Result: Decodable, Identifiable {
let id = UUID()
var address1: String
var address2: String
var address3: String
var kana1: String
var kana2: String
var kana3: String
}
}
Result
を ForEach
でループさせたいため、警告が発生しますが雑に Identifiable
へ準拠させています。
UI
SwiftUIでViewを実装し、 .task()
で住所を取得して表示します。
import SwiftUI
struct ContentView: View {
@State private var address: Address?
var body: some View {
ForEach(address?.results ?? []) { result in
VStack(alignment: .leading) {
Text(result.kana1 + result.kana2 + result.kana3)
Text(result.address1 + result.address2 + result.address3)
}
.font(.title)
}
.task {
await getAddress(zipCode: "2790031")
}
}
private func getAddress(zipCode: String) async {
// TODO: 住所を取得して `address` に格納する
}
標準
標準の URLSession
を使った実装です。
前回と異なり、非同期の data(for:) メソッドを使います。
import Foundation
// ...
private func getAddress(zipCode: String) async {
let baseUrlString = "http://zipcloud.ibsnet.co.jp"
let searchPath = "/api/search"
let searchUrl = URL(string: baseUrlString + searchPath)!
guard var components = URLComponents(url: searchUrl, resolvingAgainstBaseURL: searchUrl.baseURL != nil) else {
return
}
components.queryItems = [
.init(name: "zipcode", value: zipCode),
] + (components.queryItems ?? [])
var request = URLRequest(url: components.url!)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
do {
let (data, response) = try await URLSession.shared.data(for: request)
guard let response = response as? HTTPURLResponse,
response.statusCode == 200 else {
return
}
self.address = try JSONDecoder().decode(Address.self, from: data)
} catch {
print("Error: \(error)")
}
}
URLRequest
の生成部分は前回と変わりませんが、APIコール部分が非同期でスッキリ書けました。
非同期処理が完了すると、住所が表示されることも確認できました。
Swift HTTP Types
続いてSwift HTTP Typesを使った実装です。
import HTTPTypes
import HTTPTypesFoundation
// ...
private func getAddress(zipCode: String) async {
let baseUrlString = "http://zipcloud.ibsnet.co.jp"
var request = HTTPRequest(url: URL(string: baseUrlString)!)
request.method = .get
request.path = "/api/search?zipcode=\(zipCode)"
request.headerFields[.contentType] = "application/json"
do {
let (data, response) = try await URLSession.shared.data(for: request)
guard response.status == .ok else {
return
}
self.address = try JSONDecoder().decode(Address.self, from: data)
} catch {
print("Error: \(error)")
}
}
URLSessionを使う部分は標準と同様ですが、HTTP周りを型安全に扱えるのが優れています。
URLSessionの代わりに SwiftNIO を使うこともできます。
クエリストリングを簡単に扱う方法は用意されていない 1 ので、 path
にそのまま含めています。
おわりに
Swift HTTP Typesを使うことで、HTTP周りを型安全に扱えることがわかりました。
Alamofire のようなサードパーティ製のライブラリを使っていない場合、積極的に導入してよさそうです。
ただまだ正式リリースはされていないので、それまでは導入を待ったほうがいいかもしれません。