Load Contact Avatars Asynchronously
Contacts showed a generic person icon. Now each row downloads the contact's avatar in the background and swaps it in when it arrives, so scrolling stays smooth. Failed loads are counted in metrics so we can see how often the image host has problems, and ContactCell clears its image on reuse.
AContacts/AvatarLoader.swift1 of 3
Files Changed
Contacts/
Contacts/AvatarLoader.swift
Swift · Click a line number to comment · Tap a line to comment@@ -0,0 +1,15 @@
Added: import UIKit
Added:
Added: final class AvatarLoader {
Added: var onFailure: ((URL) -> Void)?
Added:
Added: func load(_ url: URL, completion: @escaping (UIImage) -> Void) {
Added: URLSession.shared.dataTask(with: url) { data, _, _ in
Added: guard let data, let image = UIImage(data: data) else {
Added: self.onFailure?(url)
Added: return
Added: }
Added: completion(image)
Added: }.resume()
Added: }
Added: }
Contacts/ContactsViewController.swift
Swift · Click a line number to comment · Tap a line to comment@@ -1,25 +1,33 @@
import UIKit
final class ContactsViewController: UITableViewController {
private let metrics = Metrics.shared
Added: private let avatarLoader = AvatarLoader()
private var contacts: [Contact] = []
override func viewDidLoad() {
super.viewDidLoad()
title = "Contacts"
tableView.rowHeight = 64
Added: avatarLoader.onFailure = { url in
Added: self.metrics.increment("contacts.avatar_load_failed", tags: ["host": url.host ?? "unknown"])
Added: }
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
contacts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: ContactCell.reuseIdentifier, for: indexPath) as! ContactCell
let contact = contacts[indexPath.row]
cell.nameLabel.text = contact.fullName
cell.detailLabel.text = contact.company
cell.avatarView.image = UIImage(systemName: "person.crop.circle")
Added: let url = URL(string: contact.avatarURL)!
Added: avatarLoader.load(url) { image in
Added: cell.avatarView.image = image
Added: }
return cell
}
}
Contacts/ContactCell.swift
Swift · Click a line number to comment · Tap a line to comment@@ -5,5 +5,10 @@ final class ContactCell: UITableViewCell {
@IBOutlet var avatarView: UIImageView!
@IBOutlet var nameLabel: UILabel!
@IBOutlet var detailLabel: UILabel!
Added:
Added: override func prepareForReuse() {
Added: super.prepareForReuse()
Added: avatarView.image = nil
Added: }
}