#なぜこの記事を書いたか
「RxSwift」「MVVM」で開発する事になり色々調べたがSwift4でシンプルなハンズオンがあまり無いように感じたので今回まとめました。
<今回作るアプリ>
・シンプルなチャット風UI
・上までスクロールすると自動的に前のデータをロード(無限ロード)
・テキストに入力して「send」ボタンタップでタイムラインにメッセージを追加
・LINE風のテキスト入力UI(テキストがキーボードに隠れないよう制御)
<ソースコード>
https://github.com/okamok/RxSwiftMvvmSample
下記アプリを実際に動かしたい場合に実行して下さい。
1.プロジェクトルートで下記を実行
$ pod install
2.作成された RxSwiftMvvmSample.xcworkspace
からプロジェクトを開く
3.Xcode で Run
を実行すると動作します。
#この記事でカバーされない事
「RxSwift」「MVVM」の詳細な仕様や概念については別に詳しい記事が沢山あるのでそちらを参照お願いします。
今回は最低限、下記の認識で進めたいと思います。
###RxSwiftとは?
1.ReactiveX(Reactive Extensions)のSwift実装
2.非同期処理、イベント処理をサポートするライブラリ(=コールバック地獄から解放されます)
###MVVMとは?
1.アプリを実装をModel,ViewModel,Viewのレイヤーに分けるアーキテクチャ。
2.View -> ViewModel -> Model と参照する。ViewからはModelに直接アクセスしない。
3.ViewとViewModelをデータバインドさせる。これはデータに変更があった時にUIの表示を変更する場合に必要。
4.上記3つのレイヤーに責務を切り分ける事でViewに責務が集中するのを避ける。(Fat ViewController対策)
#ハンズオン
・まずxcodeでSingl View Appの新規プロジェクト「RxSwiftMvvmSample」を作成。
・podfile
を作成。
https://github.com/okamok/RxSwiftMvvmSample/blob/master/Podfile
下記の通りRxSwift関連のライブラリが含まれている。
# RxSwift
pod 'RxSwift', '~> 4.0'
pod 'RxCocoa', '~> 4.0'
pod 'RxGesture'
pod 'RxDataSources', '~> 3.0'
pod 'RxKeyboard'
・$ pod install
を実行
・作成された RxSwiftMvvmSample.xcworkspace
からプロジェクトを開く
・MVVMに沿って実装する為、Classesフォルダの下にModel,View,ViewModel,それぞれのフォルダを作成。
##Viewを作成
Viewフォルダ配下にChatControllerView.swiftを作成。
こちらのソースをコピーします。
https://github.com/okamok/RxSwiftMvvmSample/blob/master/RxSwiftMvvmSample/Classes/View/ChatControllerView.swift
下記ChatControllerView.swift内のRxSwiftとMVVMに関する部分の解説です。
各種Rxをimportする。
import RxSwift
import RxCocoa
import RxKeyboard
import RxGesture
ViewModelをインスタンス化する。(Modelはインスタンス化しない)
private let chatViewMoel = ChatViewModel()
setRxSwift
functionでRxSwiftの実装を行なっています。
長くなるので下記リンクから参照下さい。
RxSwiftで下記の内容を実装しています。
・TableViewのデータバインド
・データソースをobserveして変更があった時、自動的に指定したプログラムが動作させる。
・スクロールをobserveして上までいったら過去データをロード。ViewModelのscrollEndComingとBindしている。
・キーボードでテキストが隠れないように制御
・送信ボタン タップ時の処理
##ViewModelを作成
ViewModelフォルダ配下にChatViewModel.swiftを作成。
こちらのソースをコピーします。
https://github.com/okamok/RxSwiftMvvmSample/blob/master/RxSwiftMvvmSample/Classes/ViewModel/ChatViewModel.swift
下記ChatViewModel.swift内のRxSwiftとMVVMに関する部分の解説です。
Modelをインスタンス化
let messageModel = MessageModel()
dataMessageRx データソースを定義(これは入れ物だけ。実態はModel内で管理されている)
//TableViewのデータソース(この内容を一覧に表示する)
var dataMessageRx:RxCocoa.BehaviorRelay<[Message]>
scrollEndComingの値を監視して スクロールで上まで行った時の処理。ここでsubscriveして観察している。
//スクロール 上まで到達した時
// driveはsubscribeと同様、イベントの購読を行う。(=ここで受信する)
scrollEndComing.asDriver()
.drive(onNext: { bool in
if (bool) {
print("TOPに達しました")
//データソースでいま何件表示しているかを確認して現ページを判定
let page:Int = self.dataMessageRx.value.count / 20
self.messageModel.currentPage = page + 1 //ページを加算
if (self.messageModel.dataMessageRx.value.count > 0) {
self.messageModel.data = [] //データ取得前に初期化
self.messageModel.messagesGet()
}
}
})
.disposed(by: disposeBag)
(参考)Driverの概念
https://qiita.com/inamiy/items/d6fa90d0401fa0e83852
##Modelを作成
Modelフォルダ配下にChatModel.swiftを作成。
こちらのソースをコピーします。
https://github.com/okamok/RxSwiftMvvmSample/blob/master/RxSwiftMvvmSample/Classes/Model/ChatModel.swift
下記ChatModel.swift内のRxSwiftとMVVMに関する部分の解説です。
structを定義
//チャット データソースとなるstruct
struct Message {
let message: String
init(message:String) {
self.message = message
}
}
dataMessageRx データソースを定義 この値に変更があるとViewModelを通してViewのUIが変更される。
var dataMessageRx = RxCocoa.BehaviorRelay<[Message]>(value: []) //TableViewのデータソース。Variableがdeprecateになったので BehaviorRelayを使用
データソースを返す
func messagesRx() -> RxCocoa.BehaviorRelay<[Message]> {
//データ取得前に現カウントと取得しておいてから初期化
self.data = []
self.messagesGet()
return self.dataMessageRx
}
ページ数に応じてデータを取得してセット。
//ページ数に応じてデータを取得
// 取得後、dataMessageRx のValueを変更する(=subscribeしている箇所を実行)
func messagesGet() {
//**** 表示するデータを取得してデータソースを更新(すると自動的にobserveしているプログラムが実行される)
// 今回は配列だが、本来はFirebaseなどのDBからデータを取得する部分。
//ページ数を考慮してデータを取得
self.data = (dataDB?.suffix(20 * currentPage).map { $0 })!
//取得したデータでobservableを変更
self.dataMessageRx.accept(self.data!)
}
データの追加
func add(msg:String){
//データソースを更新
self.dataDB?.append(Message(message: msg))
//self.data?.append(Message(message: msg))
self.messagesGet()
self.dataMessageRx.accept(self.data!)
}
##RxSwiftとは関係ない周辺のファイルを作成と修正
・Common/MessageTableViewCell.swift
を作成して下さい。
https://github.com/okamok/RxSwiftMvvmSample/blob/master/RxSwiftMvvmSample/Common/MessageTableViewCell.swift
・AppDelegate.swift
を下記のように更新して下さい。これはストーリーボードを使いたくない為の修正となります。
https://github.com/okamok/RxSwiftMvvmSample/blob/master/RxSwiftMvvmSample/AppDelegate.swift
##完了
これでXcodeでRunすると実際にアプリが動きます。
#まとめ
RxSwiftやMVVMについて調べていた時に分かりやすいハンズオンが少ないように感じたので今回まとめました。実際にチャットを作るとなるとFirebaseなどを使用すると思いますので適宜修正して頂ければと思います。
間違え等ありましたらコメント頂けますと幸いです。