Takahiro Octopress Blog

-1から始める情弱プログラミング

BrightFutureを使ってみよう!

SwiftでPromiseライブラリを使ってみよう

最近は更新頻度が落ちてしまっていますが、久々に更新します。
(iPhoneSEが発表されたことですし笑)

さて、本日はSwift版Promiseライブラリとしては割と有名な BrightFuture ライブラリを使ってみたいと思います。
筆者は、新しいことに取り組む際は、JSで例を見た方がわかりやすかったりするので、Node.jsのPromiseと交えながら紹介していきたいと思います。

Promiseとは

まずは、Promiseの存在について軽く触れておきます。
JS Promiseの本によると、Promiseとは『非同期処理を抽象化したオブジェクトとそれを操作する仕組み』のことだそうです。
これだけ読むと理解しがたいと思うので、筆者が感じるPromiseの良さをざっくり書くと…

  • 非同期処理を繰り返す場合でも、深いネストにならない
  • 非同期処理を繰り返す場合でも、見かけ上、直列的に処理を書ける

ということにつきると思います。

具体的な例については、Node.jsで説明します。

Node.jsでのPromiseの例

早速、見ていきます。
Node.jsで非同期処理を定義する場合、

1
2
3
4
5
6
// 非同期処理を持ったメソッド
function p(msg, callback) {
  setTimeout(function() {
      callback(msg);
  }, 1000);
}

となります。
このメソッドを繰り返し実行する場合、

1
2
3
4
5
6
7
8
9
p("Hello", function(msg1) {
  console.log(msg1 + ", date: " + new Date());
  p("Good Evening", function(msg2) {
      console.log(msg2 + ", date: " + new Date());
      p("Good Bye", function(msg3) {
          console.log(msg3 + ", date: " + new Date());
      });
  });
});

のようになります。
ネスト深いですね…。

これをPromiseを用いて書き直してみましょう。
まずはメソッドの定義にPromiseを適用します。

1
2
3
4
5
6
7
function p(msg) {
  return new Promise(function(resolve) {
      setTimeout(function() {
          resolve(msg);
      }, 1000);
  });
}

こうすることで、呼び出し側で下記のように浅いネストで書くことができます。

1
2
3
4
5
6
7
8
9
10
11
p("Hello").then(function(msg1) {
  console.log(msg1 + ", date: " + new Date());
  return p("Good Evening");
})
.then(function(msg2) {
  console.log(msg2 + ", date: " + new Date());
  return p("Good Bye")
})
.then(function(msg3) {
  console.log(msg3 + ", date: " + new Date());
});

うむ。かなり浅くなりましたね。
これをSwiftで書くとどうなるのでしょうか?

SwiftでのPromiseの例

Swiftでも非同期処理を定義する場合、

1
2
3
4
5
6
7
8
9
func p(msg: String, closure: (String -> ())) {
  let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
  dispatch_async(queue, {
      sleep(1)
      dispatch_sync(dispatch_get_main_queue(), {
          closure(msg)
      })
  })
}

のように書けたりします。
これを複数回呼び出す場合、

1
2
3
4
5
6
7
8
9
p("Hello") { (msg1) in
  print(msg1 + ", date: \(NSDate())")
  p("Good Evening") { (msg2) in
      print(msg2 + ", date: \(NSDate())")
      p("Good Bye") { (msg3) in
          print(msg3 + ", date: \(NSDate())")
      }
  }
}

のようになります。
同様にネストが深くなりますね…。

では、Promiseでネストを改善しましょう。
Swiftでは、OSSのBrightFutureを利用します。
導入はいつも通りCocoaPodsを使いました。

1. Podfileに下記を記載

1
2
3
4
5
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

pod 'BrightFutures'

2.pod installを実行

導入が完了したら、処理を書き換えていきます。
メソッドにPromiseを適用しましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
func p_future(msg: String) -> Future<String, NoError> {
  let promise = Promise<String, NoError>()
  let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
  
  dispatch_async(queue, {
      sleep(1)
      dispatch_sync(dispatch_get_main_queue(), {
          promise.success(msg)
      })
  })
  
  return promise.future
}

これを呼び出すときは下記のように書けます。

1
2
3
4
5
6
7
8
9
p_future("Hello").flatMap { msg1 in
  print(msg1 + ", date: \(NSDate())")
  p_future("Good Evening")
}.flatMap { msg2 in
  print(msg2 + ", date: \(NSDate())")
  p_future("Good Bye")
}.onSuccess { (msg3) in
  print(msg3)
}

ネストが浅くなりましたね。
こうして比較してみると、Node.jsとSwiftで呼び出し側の書き方はかなり似ていますね。
Swiftだけを眺めてみてもよくわからないという方はNode.jsの方も見てみると良いと思います。

今回はかなり基本的な例を書かせて頂きましたが、参考になれば幸いです。
といったところで本日はここまで。

Comments