TableViewで一番下まで行ったら次のページをロードする

この前、とある課題でニュースリーダーアプリ的なのを作った際、TableViewで一番下まで行ったら次のページをロードするのを実装しようとしたら、割とごちゃっとしてたので自分なりに整理しておきます。

インジケータが乗ったxibファイルを作る

下までスクロールしたときにくるくるしてるやつですね。
これをxibファイル作ってセルに配置して、TableViewにフッターとして埋め込みます。

// ArticleListViewController.swift

 override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.register(R.nib.loadingCell)
        let footerCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: R.reuseIdentifier.loadingCell.identifier)!
        (footerCell as! LoadingCell).startAnimation()
        let footerView: UIView = footerCell.contentView
        tableView.tableFooterView = footerView
}

一番下にたどりつく前に次のapiを叩く

一番下までスクロール仕切らないところでAPI叩いて、下までたどり着くときにちょうどParseが終わってるようにします。
(つまりさっきのインジケータが日の目を見るのはほんの一瞬)

// ArticleListViewController.swift

func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let currentOffsetY = scrollView.contentOffset.y
        let maximumOffset = scrollView.contentSize.height - scrollView.frame.height
        let distanceToBottom = maximumOffset - currentOffsetY
        if distanceToBottom < 500 && tableView.isDragging {
            self.viewModel?.search()
        }
    }

リクエスト調整

このままだと一回下までスクロールしたらそのままずっとAPI叩いちゃうので、Parseが終わるまでは次のリクエスト送らないように、以下のようなStatusを作ります。

enum LoadStatus {
    case initial
    case fetching
    case full
}

最初は.initialにしておいて、非同期処理の中で状態を設定し直してあげます。

// ArticleListViewModel.swift

func search() {
        
        guard loadStatus == .initial else {
            return
        }
        self.loadStatus = .fetching
        self.shared.sync(query: self.searchWord, page: self.page)
            .subscribe { [weak self] result in
                switch result {
                case .success(let data):
                    self?.articles.append(contentsOf: data.articles)
                    if data.articles.count != 0 {
                        self?.refreshToggle.accept(())
                        self?.page += 1
                        self?.loadStatus = .initial
                    } else {
                        self?.loadStatus = .full
                    }
                case .error(let error):
                    print(error)
                    self?.loadStatus = .initial
                }
            }.disposed(by: disposeBag)
    }

QiitaのAPIだと、一回のリクエストでは最大100件までしか取得できないので、pageって変数に先に叩いたページ保存しておいて、一回リクエエスト叩くごとに増やしていくことで順番に取得していくことができます。

ソースコードの全貌載せておきます。

github.com

参考にした記事はこちら↓

qiita.com

少なくとも私がやった感じQiitaのAPIって割とすぐに403(リクエスト送りすぎ)って返って来ちゃうのでスクロールし過ぎには注意が要りそうです、、、汗

何か気になる所ありましたら是非教えて下さい〜!!