はじめに
今回は実際にOpenWeatherMapのAPIを使ってサンプルアプリをオブジェクト指向で実装していきます。
開発環境
Xcode 13.4.1
Swift5
この記事の対象者
・SwiftでAPI通信を実装したい方
・API通信の実装方法は理解したが、オブジェクト指向での実装方法を知りたい方
・サンプルアプリを使ってみたい方
・Alamofire等のライブラリを使わず実装したい方
クラスの説明
・DataManager :APIから取得したデータを格納するためのstruct等を記述するクラス
・MainViewController :UI操作をするメインのクラス
・DataController :URLセッション等のメソッドを記述するクラス
実装手順
①Open Weather Mapのサイトに登録してキーを取得
②APIから取得したデータを格納するためのstructをDataManagerに記述
③DataControllerでURLセッション等を記述
④MainViewControllerでDataControllerのURLセッションを呼び出す
解説
①Open Weather Mapのサイトに登録してキーを取得
Open Weather Mapとは
基本的には無料で利用できる天気予報APIです。
有料版と無料版での機能を比較すると、リクエスト回数の制限や取得できる天気予報の日数に違いがありますが無料版で全然大丈夫です。
APIキー取得方法
会員登録
Open Weather MapでSign in(会員登録)→ 『Create an Account』→ 『Purpose』利用目的を記入
→『Company』は記入不要 → Saveボタン → メール認証 → 登録完了
APIキーを取得
ユーザー名をタップ → API keysタブ → Keyの下に表示されているのがあなたのAPIキーになります。
②APIから取得したデータを格納するためのstructをDataManagerに記述
1、まずDataManagerクラスを作成する
中身は空でいい。なぜならただstructをまとめて記述してるだけだから。
2、DataManagerクラスの外にDecodableを継承したstructを記述
どのファイルからでもアクセスしやすくするため
import UIKit
import Foundation
class DataManager: NSObject {
}
//mainの情報
struct main_info : Decodable{
//平均気温
let temp: Double
//湿度
let humidity: Double
}
//weatherの情報
struct weather_info : Decodable{
//天気の状態
let description: String
}
struct ReceiveData {
var statusCode: Int
var data: Data?
var result:Bool
}
③MainViewControllerでDataControllerのURLセッションを呼び出す
1、NSObjectを継承したDataControllerクラスを作成
import Foundation
let connectURL = "https://api.openweathermap.org/data/2.5/weather?"
class DataController: NSObject {
}
2、APIリクエストの準備をするメソッドを作成
class func get_tempメソッド: 引数にプロパティの値を指定できるようにする
URLQueryItem: URLのプロパティとその値を追加
request.httpMethod: API通信を記述(GET,POST,PUT)
詳しくは(超簡単)SwiftでAPIを叩いてサーバーから情報を取得する方法を参照
return session(request: request): 戻り値でsessionメソッドを呼び出す
// MARK: 指定した都道府県の天気情報を取得(リクエスト)
class func get_temp(lat: String, lon: String, appid: String) -> ReceiveData {
var urlComponents = URLComponents(string: connectURL)!
urlComponents.queryItems = [
//URLのプロパティとその値を追加
URLQueryItem(name: "lat", value: lat),
URLQueryItem(name: "lon", value: lon),
URLQueryItem(name: "appid", value: appid),
URLQueryItem(name: "lang", value: "ja"),
URLQueryItem(name: "units", value: "metric")
]
var request = URLRequest(url: urlComponents.url!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpBody = urlComponents.percentEncodedQuery?.data(using: .utf8)
return session(request: request)
}
3、非同期でデータを受信(レスポンス)
//MARK: 非同期でデータを受信(レスポンス)
class func session(request: URLRequest) -> ReceiveData {
// セマフォの準備
let semaphore = DispatchSemaphore(value: 0)
//ここでクエリ結果を受け取る
var receiveData: ReceiveData = ReceiveData(statusCode: 0, data: nil, result: false)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in //非同期で通信を行う
if ((error) != nil) {
// 受信エラー
print("受信エラー:\(String(describing: error?.localizedDescription))")
semaphore.signal()
}
else {
guard let data = data else { return }
if let response = response as? HTTPURLResponse {
receiveData.statusCode = response.statusCode
}
if receiveData.statusCode != 200 {
// 受信エラー
print("受信エラー")
semaphore.signal()
}
else {
receiveData.data = data
semaphore.signal()
}
}
}
task.resume()
semaphore.wait()
//statusCodeを出力
print(receiveData.statusCode)
//requestのURLを出力
print(request)
//取得した生のデータを出力
let str: String? = String(data: receiveData.data!, encoding: .utf8)
print(str)
return receiveData
}
全体のコードを載せておきます
import Foundation
//OpenWeatherMapにアクセスするためのURL
let connectURL = "https://api.openweathermap.org/data/2.5/weather?"
class DataController: NSObject {
//MARK: 非同期でデータを受信(レスポンス)
class func session(request: URLRequest) -> ReceiveData {
// セマフォの準備
let semaphore = DispatchSemaphore(value: 0)
//ここでクエリ結果を受け取る
var receiveData: ReceiveData = ReceiveData(statusCode: 0, data: nil, result: false)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in //非同期で通信を行う
if ((error) != nil) {
// 受信エラー
print("受信エラー:\(String(describing: error?.localizedDescription))")
semaphore.signal()
}
else {
guard let data = data else { return }
if let response = response as? HTTPURLResponse {
receiveData.statusCode = response.statusCode
}
if receiveData.statusCode != 200 {
// 受信エラー
print("受信エラー")
semaphore.signal()
}
else {
receiveData.data = data
semaphore.signal()
}
}
}
task.resume()
semaphore.wait()
//statusCodeを出力
print(receiveData.statusCode)
//requestのURLを出力
print(request)
//取得した生のデータを出力
let str: String? = String(data: receiveData.data!, encoding: .utf8)
print(str)
return receiveData
}
// MARK: 指定した都道府県の天気情報を取得(リクエスト)
class func get_temp(lat: String, lon: String, appid: String) -> ReceiveData {
var urlComponents = URLComponents(string: connectURL)!
urlComponents.queryItems = [
//URLQueryItemでURLのプロパティを追加
URLQueryItem(name: "lat", value: lat),
URLQueryItem(name: "lon", value: lon),
URLQueryItem(name: "appid", value: appid),
URLQueryItem(name: "lang", value: "ja"),
URLQueryItem(name: "units", value: "metric")
]
var request = URLRequest(url: urlComponents.url!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpBody = urlComponents.percentEncodedQuery?.data(using: .utf8)
return session(request: request)
}
}
おわりに
ここまで記事を読んでいただきありがとうございます。
今回の内容は非常に基礎だと思いますが、意外と記述の仕方に迷ったので共有しておきます。
またもっと簡単に実装した場合は(超簡単)SwiftでAPIを叩いてサーバーから情報を取得する方法を閲覧してください。