LoginSignup
17
12

More than 5 years have passed since last update.

Alamofireの処理遅延をSwift-Promisesで実装する [Swift 1.1]

Last updated at Posted at 2015-05-30

問題点

SwiftでのiOSアプリ開発において、
Alamofire*1とか通信系の関数を使用すると、
コールバックの中身の実行が通信終了を
待たずして別の処理が走ってしまう。

意図した処理が実装できず、例えば
下図のようにAPIを叩いた結果が
テーブルに表示されない悲しい事例も見られる*2

Screen_Shot_2015-05-30_at_19.54.44.png

このような事態に対応するべく、
javascript/jQuery界隈では
Deferredオブジェクトという
状態監視インスタンスの導入が利用されている。

今回はこのDeferredオブジェクトを用いて
遅延処理の実装を行う。

ゴール

Alamofireの通信終了後に
意図した処理を実行する。

方針

Deferredオブジェクトを使う。
Deferredオブジェクトは処理が
進行中/完了後/異常終了のどの状態に
あるのかを監視するオブジェクトとなっている。
通信が終了したタイミングで
状態を"完了後"とすれば、
その後に続けたい処理を書くことで
コールバックを使いながらも{通信}→{意図した処理}の
順で実装できる。

javascript, 特にjQueryではajaxとともに
よく使われるオブジェクトである。

実装内容

RokkinCat/Swift-Promises*3にあった
Classesフォルダ以下の、Promise.swiftを
アプリソースコードに追加。

Promises in Swift*4の特に下記のcode1を参考にしながら
func callAPI() -> Promiseという
関数を実装した。

code1
func uploadFile() -> Promise {
  let p = Promise.defer()
  dispatch_async(
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), 
    {
        let success = self.actualFileUpload()
        if !success {
            p.reject()
        }
        dispatch_async(dispatch_get_main_queue(), {
            p.resolve()()
        })
    })
  return p
}
MyPageViewController.swift
...
    let deferred = Deferred()
...
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        callAPI().then{(value) -> () in
            self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
            self.tableView.reloadData()
        }
    }

    func callAPI() -> Promise{
        let promise = deferred.promise
        Alamofire.request(CRUDRouter.Read(routerName: "users/1/favorites", id: 0))
            .responseJSON { (_, _, JSON, _) in
                var arr : NSArray = JSON!["comment"]!! as NSArray
                for a in arr{
                    self.items.append(a["comment"]!! as String)
                }
                self.deferred.resolve("end API calling")
        }
        return promise
    }

実装結果

下記のようなテーブルに
文章の追加された画面が、
画面表示後0.2秒程度で表示されるようになった。
Deferredを実装していない時には、
タブを行き来して2回目に表示したときに
初めて表示されていた。
(TableViewの実装方法については
ここでは割愛します。)

Screen_Shot_2015-05-30_at_20.11.20.png

これにより、Deferredを使うことで
正しく通信→処理の順に
実装できることが確かめられた。

表示されているテキストは自前の
バックエンドに対してHTTP Requestを
飛ばした結果となっている。

まとめ

本記事ではDeferredを用いた処理遅延を実装してAlamofireの通信後に意図した処理を実行させた。
むっちゃ頑張った。
SwiftとJavascriptは似ているのではないかという気もしてきた。
JSONのAPIをコールする実装を紹介する記事*5
あったが、Alamofireの方が
API周りを簡潔に書けるため、こちらのほうが
コストの低い実装となると思う。
また、他のDeferredオブジェクトの実装*6もありえたが、
今回はすぐに見つかったもので実装した。

参考文献

  1. Alamofire/Alamofire
  2. [Swift 1.1] swiftで api を叩いて、JSONをパースして、表示させる方法 (xcodeは6.1, iOSは8.1)
  3. RokkinCat/Swift-Promises
  4. Promises in Swift
  5. [Swift]APIで取得したJSONをswiftyJSONでパースして、天気情報をUITableViewで表示。お天気アプリを作ってみる。
  6. 【iOS】 コールバック地獄から脱出する為のライブラリ一覧
  7. SwiftでiOS開発:RSSリーダー
17
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
12