SwiftでAPIを使ってHTTPリクエスト
初学者向けに3つを実例で解説します:
- APIの基本的な使い方
- HTTPリクエストの作成
- MVVMモデルの実装
開発環境
- Xcode: Version 13.3
- SwiftUIを使用
作成物
コロナウイルスのデータを取得するアプリ「CovidSimple」を作成しました。ファイル構造は、モデル・ビュー・ビューモデルに分けて整理されています。
アーキテクチャ設計
MVVMモデルを採用しています。データフローは以下の通りです:
- MainViewがMainViewModelへ表示データのリクエスト
- MainViewModelがModel(TotalData)の作成
- MainViewModelがAPIServerへのリクエスト
- MainViewModelがMainViewへ応答
APIServer.swift
無料API「Covid-19-statistics」(RapidAPI提供)を使用しています。
import Foundation
final class APIService {
static let shared = APIService()
private let baseURLString = "https://covid-19-statistics.p.rapidapi.com"
private let headers = [
"X-RapidAPI-Host": "covid-19-statistics.p.rapidapi.com",
"X-RapidAPI-Key": "[Your Key Here]"
]
func fetchTotalData(completion: @escaping (Result<TotalData, Error>) -> Void) {
// 1. API取得先URLの作成
let totalURLString = baseURLString + "/reports/total"
let url = URL(string: totalURLString)
guard let url = url else {
completion(.failure(CovidError.incorrectURL))
return
}
// 2. URLリクエストの作成
var request = URLRequest(
url: url,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 100.0
)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
// 3. TASKの作成
let session = URLSession.shared
let dataTask = session.dataTask(with: request) { (data, response, error) in
if error != nil {
completion(.failure(CovidError.noDataReceived))
} else {
let decoder = JSONDecoder()
do {
let totalDataObject = try decoder.decode(
TotalDataObject.self,
from: data!
)
completion(.success(totalDataObject.data))
} catch let error {
completion(.failure(error))
}
}
}
// 4. TASKの実行
dataTask.resume()
}
}APIServer.swiftの解説
3つの主要な処理があります:
-
APIを取得するためのヘッダー作成 — HostはAPI取得先、Keyはアクセス鍵。基本的に非公開にすべきです。
-
URLリクエストの作成
var request = URLRequest(
url: url,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 100.0
)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers- データを取得するタスクの作成と実行 — 通信成功時にJSONデータをデコードしてモデルに入れ、
.successで返します。
Model
TotalDataObject構造体がメインモデルです。RapidAPIの応答構造に合わせています。
struct TotalDataObject: Codable {
let data: TotalData
}
struct TotalData: Codable {
let confirmed: Int
let deaths: Int
let confirmed_diff: Int
let deaths_diff: Int
let active: Int
let fatality_rate: Double
static let dummyData = TotalData(
confirmed: 0,
deaths: 0,
confirmed_diff: 0,
deaths_diff: 0,
active: 0,
fatality_rate: 0
)
}CodableプロトコルによってJSONデコードが可能になります。
View
MainView → TotalDataView → DataCardViewという階層構造になっています。
struct MainView: View {
@StateObject private var viewModel = MainViewModel()
let formatter = DateFormatter()
let today: String
init() {
formatter.dateFormat = "y-MM-dd"
self.today = formatter.string(from: Date())
}
var body: some View {
VStack(alignment: .leading) {
Text("世界の総数:(\(today))")
.font(.title2.bold())
.foregroundColor(.primary)
.padding(10)
TotalDataView(totalData: viewModel.totalData)
}
}
}重要なのは@StateObject private var viewModel = MainViewModel()でViewModelをインスタンス化することです。
ViewModel
ModelとView、APIをつなぐ橋渡し役です。
final class MainViewModel: ObservableObject {
@Published var totalData: TotalData = TotalData.dummyData
init() {
fetchTotalData()
}
func fetchTotalData() {
APIService.shared.fetchTotalData { result in
DispatchQueue.main.async {
switch result {
case .success(let totalData):
self.totalData = totalData
case .failure(_):
self.alertItem = AlertContext.unableToFetchTotalStats
}
}
}
}
}DispatchQueueを使う理由は、通信処理に時間がかかるため、アプリの裏側で実行する必要があるからです。
まとめ
本記事では初心者向けに以下3点を解説しました:
- APIの基本的な使い方
- HTTPリクエストの作成
- MVVMモデルの実装
