こちらの記事では外部フレームワーク(Rxswiftなど)を使用しません。
不備やもっとこうすればいいなどのご意見があれば、ぜひコメントお願いします。
参考
フロントエンドの記事ですが、やりたいことと実装は大体同じになります。
まずはこちらを読んでいただきたいです。
ユーザーが画面内のコンテンツを見たかどうかを判定する一歩上の仕組み、サービスでの活用事例
やりたいこと(上の記事にある内容と同じです)
- チャット読んだら既読にしたい
- サーバー負荷を上げたくない
#前提
・叩く既読API等が複数のチャットをまとめて叩けるものであること
(一つ一つidを指定して既読にするようなものだと、そもそも無理です。)
・UITableViewを使用していること
#実装
###・各セルがセルの下まで表示された判定
あるセルが表示され始める時にはその前のセルが表示され終わっているという判定。
willDisplayのdelegateメソッドを使い、最後の行以外の表示判定を行う。
最後の行の表示されきっているか判定としては、
UITableViewの末尾までスクロールしたかを取得すれば良い(こちらの実装を参考にしてみてください)
// セルが表示される時に呼ばれる
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
// 最初のセルについては、その前のセルが存在しないため弾く
guard indexPath.row > 0 else {
return
}
// reloadDataをすると全てが構築・破棄されることで全ての行分呼ばれるので、表示されているセル以外は弾く
guard let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows,
indexPathsForVisibleRows.contains(indexPath) else {
return
}
presenter?.didDisplayMessageCell(row: indexPath.row - 1)
}
###・サーバー負荷を考慮しまとめてAPIを叩きたい
上記の実装で既読にするチャットの情報をスクロールの都度取得できるようになったかと思います。
これをそのまま取得するたびに既読APIを叩いてしまうとサーバーの負担が大きくなってしまいます。
そのためある程度まとめて叩くようにします。
具体的にはTimerのscheduleを使います
var readTimer: Timer?
func setupTimer() {
// 1秒間隔でクロージャーの処理が走るタイマーを設定
readTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
// 既読にするチャットの数が0より大きいことを判定
// 既読のAPIを叩く
}
// タイマー起動させ、定期実行をさせる
readTimer.fire()
}
タイマーは起動し続けると常に処理が走り続けてしまうため、私が実際に実装した時は
viewWillAppearでタイマーを起動させ、
viewDidDisappearでタイマーを削除しています。
// タイマーの停止・削除
readTimer?.invalidate()
あとはタイマーの中の処理や、すでに既読のものを叩かないようにするなどの制御を入れてみてください。