SwiftでProxyパターンを使ったデータ更新ロジック


I Proxy Patternを使ったデータ更新ロジック

デザインパターンを、現実に起こりそうな事例を参照しながら学習すれば、短時間で深い理解を得られる。

Swiftで現実に起こりそうな状況を題材にしたProxy Patternの例がいまいち見つからなかったので、書いてみた。

今回は事例を元に、後述の状況下でなぜProxy Patternが有力な実装方法となるのかを説明。
その後、サンプルコードを記載する。

II 作成するシステム

要件

  • 画面を開くたびに、サーバーの情報をロードして表示する
  • 情報をロードする間、ユーザーを待たせてはいけない
  • 表示する情報は、サーバー上のものと比較して多少古くても良い

サンプル

Main Screenで左上の"Contents"ボタンを押すと、コンテンツの一覧が表示される。
コンテンツの一覧画面は、表示されるごとにサーバからUITableViewに表示するString型の配列をロードする。
 

III Proxy Patternは他の実装より優れる

非Proxy Patternでは妥協が生じる

まず、Proxy Patternを使わない2つの実装を紹介する。
これらの方法では、ユーザーを待たせる、設計が複雑になる可能性があるなどの
問題を抱えている。

シンプルな実装

シンプルで単純明快だが、ユーザーを待たせてしまう実装。

ContentsTableViewControllerは、開かれるたびにContent Classから新しい情報の取得をリクエストする。
Content Classはリクエストがあるたびに、サーバーへ接続して情報を取得して返す。
ユーザーはサーバからの情報取得が完了するまでの間、待たされる。

複雑な実装

要件を満たすが、設計が複雑になる可能性のある実装。

Appが起動するたびにバックグランドでデータを読み込み、ユーザーがContantsTableViewControllerを開いたときには、すでにデータの更新が完了しているようにする実装が考えられる。

この方法は、状況によっては設計が複雑になってしまう可能性がある。

Proxy Patternは弱点を全て克服

Proxy Patternを使うことによって、前述の2つの問題を解決することができる。
前述の"シンプルな実装"にContentProxy classを追加することで実装する。

Content Proxy Class

  1. リクエストを受けると、前回リクエストを受けた時の情報を返す。
  2. 同時にバックグランドでContent Classへアクセスして情報を取得して、それを次回のために保存する。
ContentProxy
import UIKit

class ContentProxy: NSObject, ContentProviding {

    let contentsSaveKey = "ContentsSaveKey"

    func getContents() -> AnyObject {

        let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
        dispatch_async(dispatch_get_global_queue(priority, 0)) {

            // 非同期処理で、次回アクセスされた時用のコンテンツをロード&保存
            let newContents = Content().getContents()
            NSUserDefaults.standardUserDefaults().setObject(newContents, forKey: self.contentsSaveKey)

        }

        // 前回(上記の非同期処理で)保存したコンテンツを返す。 NSUserDefaultsから取り出すので、ユーザーを待たせない。
        if let lastContents = NSUserDefaults.standardUserDefaults().objectForKey(self.contentsSaveKey) {
            return lastContents

        // 前回保存したコンテンツがない場合(Appの初回起動時など)はContent Classからコンテンツを取り出す。
        // Content Classgはサーバーから情報を取り出すので、このケースに限り、ユーザーを少し待たせることになる。
        } else {
            return Content().getContents()
        }
    }
}

ユーザーから見た挙動

  • ユーザーの閲覧する情報は、ContentsTableViewControllerを開くごとに更新される。
  • 表示される情報は、サーバーの最新のものではなく、前回アクセスした時のもの。

IV 結論

今回の方法は、要件で示したとおり、ユーザーは最新ではなく前回コンテンツ一覧を表示したときの情報を見ることになる。
ただし、コンテンツは一覧を見るたびに更新されるので、サーバーの情報とユーザーに表示される情報をシビアに同期したい場合以外では、設計を単純に保てるので有力な候補となり得る。