[Swift]UITableViewDataSourcePrefetching ํ ์ด๋ธ ๋ทฐ์ ์ ๋ฐ์ดํฐ ๋ฏธ๋ฆฌ ๋ก๋ ์ํค๊ธฐ
UITableViewDataSourcePrefetching ์ ํ๋กํ ์ฝ์ ํ ์ข ๋ฅ๋ก ์ฌ์ฉ์์ ํ๋ฉด์์ ๋ณด์ฌ์ง๊ธฐ ์ ์ ์ ์์ ์ฒ๋ฆฌํด์ผ ํ๋ ์ฐ์ฐ์ด ๊ธด ๊ฒฝ์ฐ ๋ฏธ๋ฆฌ ์ฐ์ฐ์ ์ํํ ์ ์๊ฒ ํด์ฃผ๋ DataSource ์ ๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด ๊ตณ์ด ์ ์ฌ์ฉ๋์ง ์์ง๋ง ์ด๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ธ๋ถ๋ก ๋ถํฐ ๋ฐ์์ ์ ์ ๋ณด์ฌ์ฃผ๋ ๊ฒฝ์ฐ์ ๋ํด ๊ณต๋ถํ๋ฉด์ ์๊ฒ ๋์ด ํฌ์คํ ํด๋ณผ๊น ํฉ๋๋ค.
prefetch๋?
์งํ ์ค์ธ ์ฒ๋ฆฌ์ ๋ณํํ์ฌ ํ์ํ๋ค๊ณ ์๊ฐ๋๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ ์ ํ๋ ํ๋ ๊ฒ์ ๋งํ๋ค.
- ์ถ์ฒ : ์ปดํจํฐ์ธํฐ๋ทIT์ฉ์ด๋์ฌ์
์ฌ์ฉ์ ํ๋ฉด์ ๋ณด์ฌ์ง๋ ํ ์ด๋ธ ๋ทฐ์ ๋ฐ์ดํฐ ์์ค์ ๋๋ถ์ด ์ ์์ ๋ฏธ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ์ค๋นํด์ผ ํ๊ฑฐ๋ ๋ฐ์ดํฐ๋ฅผ ์ค๋น(์ฒ๋ฆฌ)ํ๋ ์๊ฐ์ด ๊ธด ๊ฒฝ์ฐ ์ฌ์ฉํ๊ฒ ๋๋ฉฐ tableView(_:cellForRowAt:)๋ฉ์๋๊ฐ ์คํ ๋๊ธฐ ์ ์ ํ๋ฆฌํ์น๊ฐ ์ํ๋ฉ๋๋ค.
๊ทธ๋ฌ๋ DataSourcePrefetching ์ ์ฌ์ฉํ ๋ ์ฃผ์๊ฐ ํ์ํฉ๋๋ค.
Loading Data Asynchronously
์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ์ค๋นํ๊ณ ์ฒ๋ฆฌํ๊ธฐ ์ํด์ ๋น๋๊ธฐ๋ก ์ํ๋ฉ๋๋ค. ํ๋ฆฌํ์น ๋ฉ์๋๋ ๋ชจ๋ ์ ์์ ์ํ๋๋ ๊ฒ์ ์๋์ง๋ง tableView(:cellForRowAt:)์ ๋ฐ๋์ ๊ตฌํํด์ผ ํฉ๋๋ค. (prefetch๋ ๋ฐ๋์ ์๋)
๊ทธ ์ด์ ๋ ์๋์ ๊ฐ์ ์ด์ ๋๋ฌธ!
1. ํ๋ฆฌํ์น ํธ์ถ์ ํตํด ๋ฏธ๋ฆฌ ๋ก๋ ๋์๊ณ ์ด ๋ฐ์ดํฐ๊ฐ ํ๋ฉด์ ๋ณด์ฌ์ค ์ค๋น๊ฐ ๋์์(์ฌ์ฉ์์ ๊ธฐ๊ธฐ ํ๋ฉด์ ๋งํจ)
2. ๋ฐ์ดํฐ๊ฐ ํ์ฌ ํ๋ฆฌํ์น ๋ก์ง์ ์คํ ์ค์ด๋ ์์ง ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํ ์ํ
3. ํ๋ฆฌํ์น๊ฐ ์์ง ํธ์ถ๋์ง ์์์ ํ๋ฉด์ ๋ณด์ฌ์ค ์ํ๊ฐ ์ค๋น ๋์ง ์์๊ณ ๋ฐ์ดํฐ๋ ์ค๋น๋์ง ์์์๋
์ด์ ๊ฐ์ ์ด์ ๋ก tableView(:cellForRowAt:)์ ๋ฐ๋์ ํธ์ถํด ์ค์ผ ํ๊ณ ํ๋ฆฌํ์น๋ ๋น๋๊ธฐ๋ก ์ด๋ฃจ์ด์ง๊ฒ ๋ฉ๋๋ค.
์์
์ฐ์ ์ ํ์ํ ๋ณ์ ์ ์ธ๊ณผ UI ์ ํ ํด์ฃผ๊ฒ ์ต๋๋ค
var tableView : UITableView = {
let tableView = UITableView()
return tableView
}()
var dataArray = [String](repeating: "false", count: 100)
dataArray๋ String. ์ด๋ ์ด๋ก ํ ์ด๋ธ ๋ทฐ ์ ์ ๋ค์ด๊ฐ default ๊ฐ์ ๊ฐ์ง๊ณ ์์ต๋๋ค.
1. UITableViewDelegate์ UITableViewDataSource๋ฅผ ๋ฐ๋ฅด๋๋ก ํด์ค๋๋ค.
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.prefetchDataSource = self
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
self.view.addSubview(tableView)
}
delegate, dataSource, prefetchDataSourece ๊ฐ ํ๋กํ ์ฝ์ ์์ฑํ๊ณ ํด๋น ํ๋กํผํฐ์ assing ํด์ค๋๋ค.
register๋ก ์ฌ์ฌ์ฉํ ์ ์ ์์ด๋๋ฅผ ๋ฑ๋กํด ์ค๋๋ค.
2. DataSource์ Delegate
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
if dataArray[indexPath.row] != "false" {
cell.textLabel?.text = dataArray[indexPath.row]
print("cellForRowAt \(indexPath.row)")
} else {
self.prefetchCellData(indexPath)
}
return cell
}
์ ์ ๊ฐฏ์๋ dataArray์ ๊ฐฏ์ ๋งํผ 100๊ฐ ์์ฑํด์ค๋๋ค. cellForRowAt ์ ๋ณด๋ฉด ๋ฐ์ดํฐ ์ด๋ ์ด์ ๋ํดํธ ๊ฐ์ ๋น๊ตํด์ ํด๋น ์ ์ ๋ฐ์ดํฐ๊ฐ ๋ก๋ ๋ฌ๋์ง ํ๋ณํฉ๋๋ค. ๋ค๋ฅธ ์กฐ๊ฑด๋ ์๊ณ , api๋ก url์ ํตํด ์ธ๋ถ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋ ๊ฒฝ์ฐ dataTask๋ฅผ ํตํด ์์๋ณด๋ ๋ฐฉ๋ฒ๋ ์์ต๋๋ค. ๋ฐ์ดํฐ๊ฐ ๋ก๋ ๋์๋ค๋ฉด ํด๋น ์ ์ ํ ์คํธ ๋ผ๋ฒจ์ ๋ก๋๋ ๋ฐ์ดํฐ ๊ฐ์ ๋ฃ์ด์ฃผ๊ณ ์๋ ๊ฒฝ์ฐ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ๊ธฐ ์ํด prefetchCellData๋ผ๋ ํจ์๋ฅผ ํธ์ถ ํฉ๋๋ค.
cellForRowAt์์ prefetchCellData๊ฐ ํ์ํ ์ด์ ๋ ๋น๋ํ๊ณ ์ฑ์ ์คํํ ๊ฒฝ์ฐ ์ฒ์ ๋ณด์ฌ์ง๋ ์ ์ ๊ฒฝ์ฐ ์ด๋ฏธ ํ๋ฉด์ ๋ณด์ฌ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ prefetch๊ด๋ จ ๋ฉ์๋๋ฅผ ํ์ง ์์ต๋๋ค. ๋ฐ๋ผ์ ๋ง์ฝ ์ฒ์ ํ๋ฉด์ ๋ณด์ฌ์ง๋ ์ ๋ค์ด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์์ง ์์ ๊ฒฝ์ฐ prefet์์ ํธ์ถํ๋ ๋ฉ์๋๋ฅผ ์คํ ํ ์ ์๋๋ก ํด์ค๋๋ค.
3. ๋ฐ์ดํฐ ๋ก๋์ฉ ๋ฉ์๋ ์์ฑ
func prefetchCellData(_ indexPath: IndexPath) {
dataArray[indexPath.row] = "\(indexPath.row)"
DispatchQueue.main.async {
let indexPath = IndexPath(row: indexPath.row, section: 0) // ๋ก๋ํ ์ธ๋ฑ์ค ํจ์ค
if self.tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { // ๋ง์ฝ ๋ณด์ด์ง ์๋ ์
์ด๋ฉด nil์ ๋ฐํํ๋ ์ต์
๋ ์ฒด์ด๋
self.tableView.reloadRows(at: [IndexPath(row: indexPath.row, section: 0)], with: .fade) // ํด๋น ์
๋ง ๋ฆฌ๋ก๋
}
}
}
์ด ๋ฉ์๋์์๋ dataArray์ ํด๋น ์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด ์ค๋๋ค. prefetch๋ ์์ ์๊ฐํ๋ฏ์ด async๋ก ๋์ํ๊ธฐ ๋๋ฌธ์ ํ ์ด๋ธ ๋ทฐ๋ฅผ ๋ค์ ๋ฆฌ๋ก๋ ํ๋ ๊ฒฝ์ฐ DispatchQueue๋ก, UI ์์ ์ ํ๋ฏ๋ก ๋ฉ์ธ ์ค๋ ๋์์ ๋์ํด์ผ ํฉ๋๋ค.
4. prefetchDataSource
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths {
print("prefetchRowsAt \(indexPath.row)")
self.prefetchCellData(indexPath)
}
}
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths {
print("cancelPrefetchingForRowsAt \(indexPath.row)")
dataArray[indexPath.row] = "false"
}
}
์ฑ์ ์คํํ๋ฉด ํ๋ฉด์ ์๋ณด์ด๋ ์ ์ธ 19 ~ 28 ๋ฒ์งธ ์ ์ prefetchRowsAt์ ์ฝ๋๊ฐ ์คํ๋์ด ๋ฏธ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ prefetch ํ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ์ฌ๊ธฐ์ ์ cellForRowAt์ ๊ผญ ์ง์ ํด ์ค์ผ ํ๋์ง ์ ์ ์๋๋ฐ ์คํํ์ ๋ง์ ์ฌ์ฉ์์ ํ๋ฉด์ ๋ณด์ด๋ ์ ๋ค์ prefetchRowAt์ ํ์ง ์๊ณ cellForRowAt์ผ๋ก ์ ์ ๋ก๋ํ ๊ฒ์ ์ ์ ์์ต๋๋ค.
์ฌ๊ธฐ๊น์ง UITableViewDataSourcePrefetching ์ ๋ํด์ ์์๋ดค์ต๋๋ค. ์ด์ํ ๊ฒ์ด ์๊ฑฐ๋ ๊ณ ์น ๋ถ๋ถ์ด ์๋ค๋ฉด ๋๊ธ๋ก ์๋ ค์ฃผ์๋ฉด ์ ๋ง ๊ฐ์ฌํฉ๋๋ค.