Swift’te Prefetch Data Source Kullanımı

dilarany
LCW Digital
Published in
4 min readNov 21, 2023
Augusto Zambonato

Apple tarafından WWDC 2016’da “Scrolling like butter^W chunky peanut butter” ifadesiyle tanıtılan -“Scrolling like butter” ifadesinde kullanıcıların kaydırma işlemini daha akıcı ve oldukça pürüzsüz hale getirildiğinden bahsederken, “chunky peanut butter” ifadesiyle de önceden yaşanan scroll deneyimlerine göndermede bulunuluyor- iOS 10 ve sonrasını destekleyen UITableView ve UICollectionView prefetching API’ları tanıtıldı.

Prefetching, verinin henüz view tarafından talep edilmeden önce async bir şekilde önden yüklenmesi sürecidir. Kullanıcı (kullanılan yapıya göre) aşağı veya sağa doğru kaydırma işlemi yaparken, görünür hale gelecek olan sonraki cell’ler için verileri önceden yükleyerek pürüzsüz ve akıcı bir kullanıcı deneyimi sağlar ve performansı geliştirmeye yardımcı olur.

Bu yazımda, bunu sağlayabilmek için prefetch data source kavramından tableView üzerinden bahsedeceğim. Benzer adımlar collectionView için de geçerli olacaktır. İyi okumalar!

Desteklenen Platformlar
@MainActor protocol UITableViewDataSourcePrefetching
@MainActor protocol UICollectionViewDataSourcePrefetching

Prefetch’i, UITableView’de uygulayabilmek için UITableViewDataSourcePrefetching protokolüne tıpkı UITableViewDataSource ve UITableViewDelegate protokollerine uyduğumuz gibi uyarak aktif hale getirmemiz gerekiyor.

Bu da tableView’in dataSource’unun tableView(_cellForRowAt:) metodu çağrılmadan önce data’yı önceden yükleyebilmesi anlamına geliyor.

Aşağıdaki adımlar prefetchDataSource’u tableView’in destekleyebilmesi için gereklidir:

  • tableView’i ve onun gerekli dataSource’unu oluşturun.
  • UITableViewDataSourcePrefetching protokolünü benimseyen bir nesne oluşturun ve bunu tableView’in prefetchDataSource property’sine atayın.
  • tableView:prefetchRowsAtIndexPaths: metodu içerisinde belirtilen indexPath’lerdeki cell’ler için verilerin async bir şekilde yüklenmesini başlatın.
  • tableView:cellForRowAtIndexPath: dataSource metodunuzda önceden yüklenmiş verileri kullanarak cell’leri hazırlayın.
  • tableView, veri yüklemenin artık gerekli olmadığını size bildirdiğinde bekleyen veri yükleme işlemlerini tableView:cancelPrefetchingForRowsAtIndexPaths: metodunda iptal edin.

Hadi Başlayalım!

Bu örnekte, Apple’ın iTunes Music API’sini kullanarak prefetch data source kullanımını göstereceğim.

Öncelikle MusicManager adlı bir sınıfımızın olduğunu varsayalım ve bu sınıf içerisinde API isteklerini yönettiğimizi farzedelim.

ViewController içerisinde de MusicManager’ı kullanarak iTunes Music API’sine istek atıp, gelen veriyi işleyeceğim.

Öncelikle, MusicManager sınıfı ile iTunes API’sine istek atacak olan fetchMusic metodunu oluşturalım.

import Foundation

class MusicManager {
static let shared = MusicManager()

func fetchMusic(searchTerm: String, offset: Int, limit: Int, completion: @escaping (Result<[AppModel], Error>) -> Void) {
guard let url = URL(string: "https://itunes.apple.com/search?term=\(searchTerm)&offset=\(offset)&limit=\(limit)&entity=music") else {
completion(.failure(NSError(domain: "", code: -1, userInfo: nil)))
return
}

// URLSession yaparak ilgili URL'e istek atalım.
}
}
  • fetchMusic metodu string türünde searchTerm, integer türünde offset ve integer türünde limit alarak URL oluşturuyor. searchTerm, aranmak istenen anahtar kelimeyi ifade eder. offset, API’den alınacak verinin başlangıç noktasını belirtir. limit, bir API isteğinde alınacak maksimum öğe sayısını belirler.
  • Örneğin offset 0 ve limit 10 ise, API ilk seti getirir, offset 10 ise sonraki 10 sonucu getirir. Kullanıcı sayfayı aşağı kaydırdıkça offset artar ve yeni veriler yüklenir.
  • Ardından, URLSession ile bu URL’den verileri çekip, JSON olarak ayrıştırıp, ayrıştırılan veriyi Result tipiyle completion ile döner.

MusicManager sınıfımızı oluşturduk. Şimdi de ViewController’ımızı oluşturup, TableView’imizi tanımlayıp, gerekli dataSource’ları oluşturalım.

class ViewController: UIViewController {
let tableView = UITableView()

override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
}

func setupTableView() {
tableView.dataSource = self
tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
view.addSubview(tableView)
tableView.frame = view.bounds
}
}

// UITableView DataSource and Delegate methods
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//
}
}

Daha sonrasında ViewController’ımızın prefetching yapabilmesi için prefetchDataSource’unu aktif edip, UITableViewDataSourcePrefetching protokolünü benimsetelim.

func setupTableView() {
//
tableView.prefetchDataSource = self
}
// UITableViewDataSourcePrefetching methods
extension ViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
//
}
}

Artık API’ye istek atabiliriz. Şimdi MusicManager’ı kullanarak istek atalım.

Öncelikle gerekli property’leri ViewController sınıfımıza ekleyelim.

var apps = [AppModel]()
var offset = 0
let limit = 10

Daha sonrasında fetchApps metodunu ekleyip, MusicManager içerisindeki fetchMusic metodunu kullanarak API’ye istek atalım ve ilgili metodu viewDidLoad içerisinde çağırarak API’ye istek atalım.

func fetchApps() {
MusicManager.shared.fetchMusic(searchTerm: "music", offset: offset, limit: limit) { [weak self] result in
guard let self = self else { return }
DispatchQueue.main.async {
switch result {
case .success(let newApps):
self.apps.append(contentsOf: newApps)
self.tableView.reloadData()
case .failure(let error):
print("Error fetching music: \(error)")
}
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
//
fetchApps()
}

Ve artık sona geldik, prefetchRowsAt metodumuzun implementasyonunu yapabiliriz.

// UITableViewDataSourcePrefetching methods
extension ViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
if indexPaths.contains(where: { $0.row >= self.apps.count - 1 }) {
offset += limit
fetchApps()
}
}
}

Burada verilen indexPaths dizisinde, yüklenmiş verilerin sonuna yakın olan cell’lerin olup olmadığını kontrol ediyoruz. Eğer kullanıcı UITableView’ın aşağılarına doğru ilerliyorsa ve son cell’e yaklaşıyorsa, bu koşul true döner. $0.row; şu anki IndexPath’in satır numarasını; self.apps.count — 1 ise mevcut yüklenmiş veri listesinin son elemanının indeksidir.

Sonuç

Bu yazımda, UITableViewDataSourcePrefetching’in iOS uygulamalarındaki önemini ve bu özelliğin nasıl uygulanabileceğini detaylı bir şekilde ele aldık. Ön yükleme (prefetching) özelliği, özellikle büyük veri setlerine veya ağ üzerinden yüklenen içeriklere sahip uygulamalar için kullanıcı deneyimini önemli ölçüde iyileştirebilmektedir.

Bu teknik sayesinde, veriler cell’lere gösterilmeden önce async olarak yüklenir, böylece kullanıcıların akıcı ve kesintisiz bir kaydırma deneyimi yaşaması sağlanır. İlgili metodların kullanımı kaynak yönetimi ve performans optimizasyonu açısından kritik öneme sahiptir.

Bu metodları kullanarak veri yükleme süreçlerini daha verimli hale getirebilir ve gereksiz yüklemeleri önleyerek uygulamanın genel performansını arttırabiliriz.

Umarım bu bilgiler, sizin uygulamalarınızda daha iyi bir performans ve kullanıcı deneyimi sağlamanıza yardımcı olur. :)

MovementMemes

referanslar:

https://developer.apple.com/videos/play/wwdc2016/219/

https://developer.apple.com/documentation/uikit/uitableviewdatasourceprefetching

https://fluffy.es/prefetching/

--

--