TTBaseUITableViewController

ViewController

Pre-configured UITableViewController base class that extends TTBaseUIViewController's lifecycle hooks into a table-based screen. Provides built-in empty state view, loading indicator, pull-to-refresh, keyboard dismiss on scroll, and correct content insets automatically.


🔄 Lifecycle Methods

Override these methods — all are called at the right time in viewDidLoad/viewWillAppear:

class OrderListVC: TTBaseUITableViewController {

    // 1. Register cells, set dataSource/delegate
    override func setupData() {
        tableView.register(OrderCell.self, forCellReuseIdentifier: "OrderCell")
        tableView.dataSource = self
        tableView.delegate = self
    }

    // 2. Apply theming or extra UI setup
    override func setupUI() {
        navigationItem.title = "My Orders"
    }

    // 3. Fetch data
    override func fetchData() {
        showLoadingState()
        viewModel.fetchOrders { [weak self] in
            self?.hideLoadingState()
            self?.tableView.reloadAsyncData()
        }
    }

    // 4. Pull-to-refresh callback
    override func onRefreshData(_ sender: UIRefreshControl) {
        fetchData()
    }
}

🚀 Usage

Empty State

class WishlistVC: TTBaseUITableViewController {
    override func setupData() {
        viewModel.onDataLoaded = { [weak self] items in
            if items.isEmpty {
                self?.setEmptyView(
                    title: "No Items Yet",
                    message: "Add products to your wishlist to see them here.",
                    icon: .heart
                )
            } else {
                self?.removeEmptyView()
                self?.tableView.reloadAsyncData()
            }
        }
    }
}

Loading State

class ProductListVC: TTBaseUITableViewController {
    func loadProducts() {
        // Shows spinner overlay on table
        showLoadingState()

        APIService.getProducts { [weak self] result in
            DispatchQueue.main.async {
                self?.hideLoadingState()
                switch result {
                case .success(let items):
                    self?.products = items
                    self?.tableView.reloadData()
                case .failure(let error):
                    self?.showErrorAlert(message: error.localizedDescription)
                }
            }
        }
    }
}

📖 Override Points

MethodWhen CalledOverride For
setupData()viewDidLoadCell registration, bindings
setupUI()viewDidLoadNavigation bar, title, bar buttons
fetchData()viewWillAppearInitial data load
onRefreshData(_:)Pull-to-refresh triggeredReload data on pull
showLoadingState()ManualShow activity indicator overlay
hideLoadingState()ManualRemove activity indicator
setEmptyView(title:message:icon:)ManualShow empty state when no data
removeEmptyView()ManualRemove empty state when data arrives
Pro Tip: Use TTBaseUITableViewController with the diff-based reload pattern: in fetchData(), show loading, fetch, then call tableView.reloadAsyncData() — all thread-safe. The pull-to-refresh is auto-wired through onRefreshData.