SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

RxSwiftで一歩進んだiOSアプリ開発

MVVMモデルをもっと便利に使ってみよう(後編)~ViewModelの変数と具体的な処理方法

RxSwiftで一歩進んだiOSアプリ開発 第6回

  • X ポスト
  • このエントリーをはてなブックマークに追加

サンプルの各クラスを作成する(2)

Viewの作成

 今回のサンプルでは、作成するクラスがビューコントローラーとコレクションのセルの2つあります、先にビューコントローラーから作成します。次のようにUIとViewModelのインスタンスを定義します。

[リスト8] ViewController.swift
class ViewController: UIViewController {
    @IBOutlet weak var textField: UITextField!                               // 入力欄
    @IBOutlet weak var searchButton: UIButton!                           // 検索ボタン
    @IBOutlet weak var collectionView: UICollectionView!            // コレクション
    @IBOutlet weak var indicatorView: UIActivityIndicatorView!    // インジゲーター
    private let viewModel = SearchViewModel()                             // ViewModel
    private let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.bind()
# 略

 SearchViewModelクラスの各プロパティとは、bind()メソッドの中で行います。

[リスト9]ViewController.swift
func bind() {
     // 入力欄の値とViewModelのinputs.searchWordをbind
    self.textField.rx.text.orEmpty
          .bind(to: self.viewModel.inputs.searchWord).disposed(by: self.disposeBag)

     // 検索ボタンのタップでViewModelのinputs.searchTriggerを起動
    self.searchButton.rx.tap.asDriver()
        .drive(self.viewModel.searchTrigger).disposed(by: self.disposeBag)

     // ViewModelのoutputs.isSearchButtonEnabledと検索ボタンの押下可否をbind
    self.viewModel.outputs.isSearchButtonEnabled.asObservable()  // ------(1)
        .bind(to: self.searchButton.rx.isEnabled).disposed(by: self.disposeBag)

    // ViewModelのoutputs.itemsをコレクションビューにbind
    self.viewModel.outputs.items.asObservable()
        .bind(to: self.collectionView.rx.items(cellIdentifier: "ImageItemCell", cellType: ImageItemCell.self)) {
            (index, element, cell) in
                cell.configure(URL(string: element.src)!) // ------(4)セル内で画像を表示
        }.disposed(by: self.disposeBag)

    // ViewModelのoutputs.isLoadingとインジゲーターのisAnimatingをbind
    self.indicatorView.hidesWhenStopped = true  // インジゲーターは回っていない時は非表示に
    self.viewModel.outputs.isLoading  // ------(2)
        .bind(to: self.indicatorView.rx.isAnimating).disposed(by: self.disposeBag)

    // エラーがあれはアラートで内容を表示
    self.viewModel.outputs.error.subscribe(onNext: { [weak self] error in  // ------(3)
            let alert = UIAlertController(title: "エラー", message: error.localizedDescription, preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
            self?.present(alert, animated: true, completion: nil)
        })disposed(by: self.disposeBag)
}

 SearchViewModelクラスの各プロパティは、inputs/outputsのプレフィックスがつくのでわかりやすいです。データバインドを行う際、inputsの場合はViewのUIの後に、outputsの場合はUIより先に、位置します。連載第4回のサンプルと処理がかぶるものは説明を割愛します。

 outputs.isSearchButtonEnabledプロパティは検索ボタンのrx.isEnabledプロパティとバインドします(1)。こうすることで、ViewModelの処理からUIの動きを制御できます。同様に、outputs.isLoadingとインジゲーターのisAnimatingプロパティを結合して、Actionクラスの処理中にインジゲーターが回るようにできます(2)。outputs.errorプロパティを監視しておくことで、エラーが発生した場合の処理も他の処理と同様に定義できます(3)。また、Actionクラスを利用することで、処理中/エラー発生時の処理も他のプロパティと同様に行うことができます。

 コレクションビューのセルでは、(4)の通りconfigure(_ :)メソッドで画像を表示できるようにします。

[リスト10] ImageItemCell.swift
import UIKit
import AlamofireImage

class ImageItemCell: UICollectionViewCell {
    @IBOutlet weak var imageView: UIImageView!

    func configure(_ url: URL) {
        self.imageView.af_setImage(withURL: url)
    }
}

 画像を表示する際には、AlamofireImageライブラリのaf_setImage(withURL:)メソッドで行います。UIImageViewの拡張メソッドで、画像のURLを渡すと非同期に画像を取得して表示できます。

 サンプルを実行すると次のように動作を確認できます。

Yahoo!画像検索を利用した画像検索アプリ
Yahoo!画像検索を利用した画像検索アプリ

まとめ

 連載最終回では、RxSwift+MVVMをよりわかりやすく、より短い処理で機能を実装できるライブラリを利用しました。RxSwiftもMVVMモデルも、アプリ開発の効率をあげる点では非常に優秀です。ですが、両方とも導入しただけではまだ不十分なこともあります。RxSwift Communityなどのライブラリ開発コミュニティでは、既存ライブラリの不足する機能を補うための開発が活発に行われています。こういった派生ライブラリを取り入れることで、より良いアプリ開発を行うこともできます。みなさんもぜひ利用してみてください。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
RxSwiftで一歩進んだiOSアプリ開発連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 片渕 彼富(カタフチ カノトミ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/11340 2019/01/31 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング