Practical examples of UITableView implementation in Swift for modern iOS apps
Before getting clever, you need a clean, minimal baseline. This is the smallest practical example of UITableView implementation in Swift that still respects modern patterns.
import UIKit
final class SimpleListViewController: UIViewController {
private let tableView = UITableView(frame: .zero, style: .plain)
private let items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]
override func viewDidLoad() {
super.viewDidLoad()
title = "Fruits"
view.backgroundColor = .systemBackground
tableView.dataSource = self
tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
}
extension SimpleListViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
items.count
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
var config = cell.defaultContentConfiguration()
config.text = items[indexPath.row]
cell.contentConfiguration = config
return cell
}
}
extension SimpleListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
print("Selected: \(items[indexPath.row])")
}
}
This is the reference point. Every other example of UITableView implementation in Swift builds on this pattern: set up the table, adopt UITableViewDataSource and UITableViewDelegate, and keep your configuration inside cellForRowAt.
Real examples: grouped sections, headers, and footers
Most real apps don’t show a flat list. Settings, profile screens, and form‑like layouts use grouped sections. These examples of UITableView implementation in Swift show how to structure data and customize headers and footers.
struct SettingsSection {
let title: String
let items: [String]
}
final class SettingsViewController: UIViewController {
private let tableView = UITableView(frame: .zero, style: .insetGrouped)
private let sections: [SettingsSection] = [
.init(title: "Account", items: ["Profile", "Password", "Two-Factor Auth"]),
.init(title: "Notifications", items: ["Push", "Email", "SMS"]),
.init(title: "About", items: ["Terms of Service", "Privacy Policy"])
]
override func viewDidLoad() {
super.viewDidLoad()
title = "Settings"
view.backgroundColor = .systemBackground
tableView.dataSource = self
tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
view.addSubview(tableView)
tableView.frame = view.bounds
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
}
extension SettingsViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int { sections.count }
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
sections[section].items.count
}
func tableView(_ tableView: UITableView,
titleForHeaderInSection section: Int) -> String? {
sections[section].title
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
var config = cell.defaultContentConfiguration()
config.text = sections[indexPath.section].items[indexPath.row]
cell.accessoryType = .disclosureIndicator
cell.contentConfiguration = config
return cell
}
}
This is one of the best examples of how to map your model to sections cleanly: a struct for each section, no magic numbers, and titles coming from data instead of hard‑coded switches.
Custom cell example of UITableView implementation in Swift
The moment you need avatars, subtitles, or custom layouts, stock cells stop cutting it. Here’s a custom cell example of UITableView implementation in Swift using Auto Layout and modern configuration.
final class UserCell: UITableViewCell {
private let avatarImageView = UIImageView()
private let nameLabel = UILabel()
private let subtitleLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupViews() {
avatarImageView.layer.cornerRadius = 20
avatarImageView.clipsToBounds = true
avatarImageView.backgroundColor = .secondarySystemBackground
nameLabel.font = .preferredFont(forTextStyle: .headline)
subtitleLabel.font = .preferredFont(forTextStyle: .subheadline)
subtitleLabel.textColor = .secondaryLabel
let stack = UIStackView(arrangedSubviews: [nameLabel, subtitleLabel])
stack.axis = .vertical
stack.spacing = 2
contentView.addSubview(avatarImageView)
contentView.addSubview(stack)
avatarImageView.translatesAutoresizingMaskIntoConstraints = false
stack.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
avatarImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
avatarImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
avatarImageView.widthAnchor.constraint(equalToConstant: 40),
avatarImageView.heightAnchor.constraint(equalToConstant: 40),
stack.leadingAnchor.constraint(equalTo: avatarImageView.trailingAnchor, constant: 12),
stack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
stack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
stack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8)
])
}
func configure(name: String, subtitle: String) {
nameLabel.text = name
subtitleLabel.text = subtitle
}
}
struct User {
let name: String
let role: String
}
final class UsersViewController: UIViewController {
private let tableView = UITableView()
private let users: [User] = [
.init(name: "Alice", role: "Admin"),
.init(name: "Bob", role: "Editor"),
.init(name: "Carol", role: "Viewer")
]
override func viewDidLoad() {
super.viewDidLoad()
title = "Team"
view.backgroundColor = .systemBackground
tableView.dataSource = self
tableView.delegate = self
tableView.register(UserCell.self, forCellReuseIdentifier: "UserCell")
view.addSubview(tableView)
tableView.frame = view.bounds
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
}
extension UsersViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
users.count
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(
withIdentifier: "UserCell",
for: indexPath
) as? UserCell else {
return UITableViewCell()
}
let user = users[indexPath.row]
cell.configure(name: user.name, subtitle: user.role)
return cell
}
}
Among the best examples of UITableView implementation in Swift, this pattern scales nicely when you later add async image loading, skeleton states, or accessibility tweaks.
Editable rows: swipe to delete and insert
Users expect to manage lists: deleting emails, removing items from a cart, or reordering favorites. These real examples of UITableView implementation in Swift show how to support editing without turning your controller into spaghetti.
final class EditableListViewController: UIViewController {
private let tableView = UITableView()
private var items = ["Milk", "Eggs", "Bread", "Coffee"]
override func viewDidLoad() {
super.viewDidLoad()
title = "Groceries"
navigationItem.rightBarButtonItem = editButtonItem
tableView.dataSource = self
tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
view.addSubview(tableView)
tableView.frame = view.bounds
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
tableView.setEditing(editing, animated: animated)
}
}
extension EditableListViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
items.count
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
var config = cell.defaultContentConfiguration()
config.text = items[indexPath.row]
cell.contentConfiguration = config
return cell
}
func tableView(_ tableView: UITableView,
canEditRowAt indexPath: IndexPath) -> Bool {
true
}
func tableView(_ tableView: UITableView,
commit editingStyle: UITableViewCell.EditingStyle,
forRowAt indexPath: IndexPath) {
switch editingStyle {
case .delete:
items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
case .insert:
items.insert("New Item", at: indexPath.row)
tableView.insertRows(at: [indexPath], with: .automatic)
default:
break
}
}
}
extension EditableListViewController: UITableViewDelegate {}
If you’re building a to‑do list or habit tracker, this is one of the best examples to start from: editing is fully integrated with your data model, not just the UI.
Diffable data source: modern examples of UITableView implementation in Swift
Starting in iOS 13, Apple introduced diffable data sources, and by 2024 this should be your default for any non‑trivial list. These examples of UITableView implementation examples in Swift with diffable data reduce bugs around index paths and batch updates.
final class DiffableListViewController: UIViewController {
enum Section { case main }
struct Item: Hashable {
let id = UUID()
let title: String
}
private let tableView = UITableView()
private lazy var dataSource = UITableViewDiffableDataSource<Section, Item>(
tableView: tableView
) { tableView, indexPath, itemIdentifier in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
var config = cell.defaultContentConfiguration()
config.text = itemIdentifier.title
cell.contentConfiguration = config
return cell
}
private var items: [Item] = [
.init(title: "Inbox"),
.init(title: "Today"),
.init(title: "Upcoming")
]
override func viewDidLoad() {
super.viewDidLoad()
title = "Diffable"
view.backgroundColor = .systemBackground
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
view.addSubview(tableView)
tableView.frame = view.bounds
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
applySnapshot(animatingDifferences: false)
}
private func applySnapshot(animatingDifferences: Bool = true) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.main])
snapshot.appendItems(items, toSection: .main)
dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
}
func addItem(title: String) {
items.append(.init(title: title))
applySnapshot()
}
}
For lists that change often — think chats, notifications, or live data — this example of UITableView implementation in Swift with a diffable data source is significantly easier to maintain than manual insertRows and deleteRows calls.
For background reading, Apple’s own documentation on diffable data sources is still worth a look: https://developer.apple.com/documentation/uikit/uitableviewdiffabledatasource
Async data loading: networking and pagination
In 2024 and 2025, almost every table you build talks to an API. Here’s how you might structure real examples of UITableView implementation in Swift that fetch data asynchronously using async/await.
struct Post: Decodable, Hashable {
let id: Int
let title: String
}
final class PostsViewController: UIViewController {
enum Section { case main }
private let tableView = UITableView()
private lazy var dataSource = UITableViewDiffableDataSource<Section, Post>(
tableView: tableView
) { tableView, indexPath, post in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
var config = cell.defaultContentConfiguration()
config.text = post.title
cell.contentConfiguration = config
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
title = "Posts"
view.backgroundColor = .systemBackground
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
view.addSubview(tableView)
tableView.frame = view.bounds
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
Task { await loadPosts() }
}
private func apply(posts: [Post]) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Post>()
snapshot.appendSections([.main])
snapshot.appendItems(posts)
dataSource.apply(snapshot, animatingDifferences: true)
}
private func loadPosts() async {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
do {
let (data, _) = try await URLSession.shared.data(from: url)
let posts = try JSONDecoder().decode([Post].self, from: data)
await MainActor.run { self.apply(posts: posts) }
} catch {
print("Failed to load posts: \(error)")
}
}
}
This example of UITableView implementation in Swift is closer to production reality: network calls, error handling, and UI updates marshaled back to the main actor.
If you’re thinking about performance and responsiveness, Apple’s Human Interface Guidelines are still the north star for list design and perceived speed: https://developer.apple.com/design/human-interface-guidelines/ios/overview/themes
Accessibility and dynamic type in table views
UITableView can easily become an accessibility nightmare if you ignore Dynamic Type and VoiceOver. Modern examples of UITableView implementation in Swift should always respect content size categories and provide clear labels.
Key practices you can adopt in all the examples include:
- Use
preferredFont(forTextStyle:)for labels inside cells so text scales with user settings. - Avoid hard‑coded cell heights; let Auto Layout drive row height with
tableView.rowHeight = UITableView.automaticDimensionand a reasonableestimatedRowHeight. - Provide
accessibilityLabelandaccessibilityHintfor custom cells where the visual layout doesn’t map 1:1 to text.
If you want to go deeper into accessible list design, the W3C Web Accessibility Initiative has guidelines that translate well to mobile list views: https://www.w3.org/WAI/fundamentals/accessibility-intro/
When you review examples of UITableView implementation examples in Swift, ask yourself: will this still work for someone using the largest text size or VoiceOver? If not, fix it before you ship.
UITableView vs SwiftUI List in 2024–2025
You might wonder why you should care about more examples of UITableView implementation in Swift when SwiftUI exists. In 2024 and 2025, teams typically land on a hybrid approach:
- UIKit (UITableView) for legacy screens, highly customized layouts, or when you need mature APIs like diffable data sources and fine‑grained control.
- SwiftUI
Listfor new features where you can accept some constraints in exchange for faster iteration.
The good news: the patterns you learn from these examples include clean separation of data, cell configuration, and navigation. Those ideas transfer directly to SwiftUI’s List and ForEach.
For long‑lived apps in regulated spaces (finance, health, education), UIKit is still heavily used because of its stability and long track record. If you’re in those industries, studying practical examples of UITableView implementation in Swift is still very much worth your time.
FAQ: common questions about UITableView examples
Q: Where can I find more real examples of UITableView implementation in Swift?
Apple’s sample code on the Developer site is still the best starting point, especially projects that demonstrate diffable data sources and list layouts: https://developer.apple.com/sample-code. From there, GitHub is full of open‑source apps that show another example of production‑grade table view setups.
Q: What is a good example of migrating from a legacy UITableView to a diffable data source?
A practical migration path is to keep your existing UITableView and delegate methods for layout, but replace DataSource methods with UITableViewDiffableDataSource. Start by defining a section enum and an item type that conforms to Hashable, then gradually replace calls to reloadData() with snapshot updates. One of the best examples is Apple’s own diffable sample projects, which show step‑by‑step adoption.
Q: Do these examples include support for pagination and infinite scrolling?
The async posts example is a starting point. To add pagination, watch for willDisplay on the last row, trigger another network request, append new items to your array, and re‑apply the diffable snapshot. Many production apps follow this pattern with a loading indicator cell at the bottom.
Q: Are there examples of UITableView implementation in Swift that work well with MVVM or Clean Architecture?
Yes. The key idea is to move data fetching and transformation into a view model, then expose a simple array or snapshot description to the view controller. Your table view code becomes mostly about binding: when the view model publishes a new snapshot, you apply it. The diffable examples of UITableView implementation in Swift in this article adapt very well to MVVM.
Q: When should I choose UICollectionView instead of UITableView for lists?
If your layout is strictly one column, vertically scrolling, and mostly text, UITableView is still fine. Once you need grid layouts, complex compositional sections, or horizontally scrolling sub‑sections, UICollectionView is a better fit. The mental model you learn from these table view examples carries over to collection views pretty naturally.
Related Topics
Practical examples of simple calculator app examples in Swift
Practical examples of Swift async/await examples for asynchronous programming
Best examples of practical examples of using closures in Swift
Practical examples of UITableView implementation in Swift for modern iOS apps
Practical examples of timer examples with Grand Central Dispatch in Swift
The best examples of error handling in Swift: 3 practical examples you’ll actually use
Explore More Swift Code Snippets
Discover more examples and insights in this category.
View All Swift Code Snippets