【swift】api通信処理を共通化/エラー処理もちゃんとやる
はじめに
以前、こちらで同じようなことを書いたのですが。
そのときはまだswiftやりたてというのとエラー処理とかちゃんと考慮できてなかったので今回もう1度ちゃんとまとめてみようと思いました
実装したい機能としては以下
>サーバーからのレスポンス
array(
"status" => "OK" // または"NG" バリデーション等でエラーの場合はNGをかえす
"result" => array(
"friends" => array("id" => "1", "友達1", ・・・)
),
)こんな感じでjson形式でかえす
>クライアント側
・サーバーエラー(HTTPステータス200以外)に対応
・タイムタウトに対応
・アプリケーションエラーに対応(処理としては正常だけどアプリケーションがNGを返す)
・エラー時はデフォルトで、アラートダイアログまたはトーストを表示
ライブラリのインストール
今回つかったライブラリは以下
・AFNetworking
・Toast
Podfileを作って、pod installを実行
use_frameworks! pod 'AFNetworking' pod 'Toast'
実装
ディレクトリ構成
├── Api
│ ├── ApiBase.swift // 共通処理をココにまとめる
│ ├── ApiSample.swift // 用途ごとにファイルを追加していく
│ ├── ・・・
├── Controller
│ ├── ViewController.swift // Api以下のクラスを呼び出してAPIへアクセス
ApiBase.swift
import UIKit
import AFNetworking
import Toast
class ApiBase: NSObject {
let API_URL = "http://example.com"
let API_USER = "user"
let API_PASS = "pass"
let API_TIMEOUT: Double = 20 // タイムアウト
func callApi(callback:((result: Dictionary<String, AnyObject>?)->Void)?, callbackAppError:(()->Void)?, callbackStatusError:(()->Void)?, errorType: Int?, url: String, params: Dictionary<String,AnyObject>?) {
let manager: AFHTTPRequestOperationManager = AFHTTPRequestOperationManager()
manager.requestSerializer.setAuthorizationHeaderFieldWithUsername(API_USER, password: API_PASS)
manager.requestSerializer.timeoutInterval = API_TIMEOUT
manager.POST(API_URL + url, parameters: params,
success: {(operation: AFHTTPRequestOperation!, responsobject: AnyObject!) in
if let status = responsobject["status"] as? String {
if status == "OK" {
callback?(result: responsobject["result"] as? Dictionary<String, AnyObject>)
} else if status == "NG" {
if let message = responsobject["message"] as? String {
if 0 < message.characters.count {
if errorType == 1 {
self.showAlert(message)
} else if errorType == 2 {
self.showToast(message)
}
}
}
callbackAppError?()
}
}
},
failure: {(operation: AFHTTPRequestOperation?, error: NSError?) in
let message = "サーバーに接続できませんでした"
if errorType == 1 {
self.showAlert(message)
} else if errorType == 2 {
self.showToast(message)
}
callbackStatusError?()
}
)
}
private func showAlert(message: String) {
let alert = UIAlertView(title: "エラー", message: message, delegate: self, cancelButtonTitle: "OK")
alert.show()
}
private func showToast(message: String) {
let vc = UIApplication.sharedApplication().keyWindow?.rootViewController
vc?.view.makeToast(message, duration: 1.0, position: CSToastPositionBottom)
}
}・Api***.swiftはこのクラスを継承して追加してゆく
・エラー時のコールバックはそれぞれ必要であれば実装する形とする
ApiSample.swift
import UIKit
class ApiSample: ApiBase {
// 正常の場合のみコールバック指定
func get(callback:(result: Dictionary<String,AnyObject>?)->Void, params: Dictionary<String,AnyObject>?) {
callApi({ (result) -> Void in
callback(result: result)
}, callbackAppError: nil, callbackStatusError: nil, errorType: 2, url: "/test.php", params: params)
}
// 正常、エラーの場合でコールバック指定
func get(callback:(result: Dictionary<String,AnyObject>?)->Void, callbackAppError:()->Void, callbackStatusError:()->Void, params: Dictionary<String,AnyObject>?) {
callApi({ (result) -> Void in
callback(result: result)
}, callbackAppError: { () -> Void in
callbackAppError()
}, callbackStatusError: { () -> Void in
callbackStatusError()
}, errorType: 1, url: "/test.php", params: params)
}
}
||<
ViewController.swift
>||
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
ApiSample().get({ (result) -> Void in
print(result)
}, params: nil)
}
}