【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) } }