Closures trong Swift: Giải pháp hiện đại thay thế Delegation Pattern cho iOS

Giới thiệu về Closures Framework

Closures là một thư viện Swift được thiết kế dành riêng cho nền tảng iOS, mang đến khả năng xử lý sự kiện thông qua closure thay vì sử dụng các pattern truyền thống như delegation hay target-action. Thư viện này mở rộng chức năng cho hầu hết các class trong UIKit và Foundation, giúp mã nguồn trở nên gọn gàng và dễ bảo trì hơn.

Vấn đề với Delegation Pattern truyền thống

Trong phát triển ứng dụng iOS, delegation là một pattern phổ biến nhưng tồn tại một số nhược điểm:

  • Cần định nghĩa protocol và implement các method delegate
  • Mã nguồn bị phân tán qua nhiều file
  • Khó theo dõi luồng xử lý sự kiện
  • Boilerplate code nhiều và lặp lại

Closures giải quyết như thế nào?

Thư viện cung cấp cơ chế callback trực tiếp thông qua closure, cho phép viết mã theo phong cách functional programming với khả năng chain methods liền mạch.

Xử lý sự kiện UI Controls

Thay vì sử dụng @IBAction hoặc addTarget, bạn có thể gắn handler trực tiếp:

let submitButton = UIButton()
submitButton.onTap { [weak self] in
    self?.handleSubmit()
}

let toggleSwitch = UISwitch()
toggleSwitch.onChange { [weak self] isEnabled in
    self?.updateSetting(enabled: isEnabled)
}

Gesture Recognizers

Thêm gesture trở nên đơn giản hơn nhiều:

let contentView = UIView()
contentView.addSwipeGesture(direction: .left) { [weak self] recognizer in
    self?.navigateToPreviousScreen()
}

contentView.addLongPressGesture { recognizer in
    if recognizer.state == .began {
        print("Long press detected")
    }
}

Làm việc với UITableView

Cung cấp hai cách tiếp cận: đơn giản hóa với collection binding hoặc linh hoạt với closure-based delegates:

Phương pháp 1 - Collection Binding:

struct Product {
    let name: String
    let price: Double
}

let products: [Product] = loadProducts()

tableView.bind(products, cellType: ProductCell.self) { product, cell, indexPath in
    cell.configure(with: product)
}

Phương pháp 2 - Granular Control:

tableView.register(ProductCell.self, forCellReuseIdentifier: "ProductCell")

tableView
    .setNumberOfRows { _ in
        return self.products.count
    }
    .setConfigureCell { indexPath in
        let cell = tableView.dequeueReusableCell(
            withIdentifier: "ProductCell", 
            for: indexPath
        ) as! ProductCell
        cell.configure(with: self.products[indexPath.row])
        return cell
    }
    .setDidSelectRow { indexPath in
        self.showProductDetail(at: indexPath.row)
    }
    .setHeightForRow { indexPath in
        return indexPath.row == 0 ? 80 : 60
    }

UICollectionView

Tương tự, UICollectionView cũng được hỗ trợ đầy đủ:

collectionView.register(PhotoCell.self, forCellWithReuseIdentifier: "PhotoCell")

collectionView
    .setNumberOfItems { _ in
        return self.photos.count
    }
    .setConfigureItem { indexPath in
        let cell = collectionView.dequeueReusableCell(
            withReuseIdentifier: "PhotoCell", 
            for: indexPath
        ) as! PhotoCell
        cell.imageView.image = self.photos[indexPath.item]
        return cell
    }
    .setDidSelectItem { indexPath in
        self.openPhotoViewer(for: indexPath.item)
    }

Tính năng nâng cao

UIImagePickerController

Đây là một trong những tính năng hữu ích nhất, loại bỏ hoàn toàn delegate pattern phức tạp:

let imagePicker = UIImagePickerController(
    sourceType: .photoLibrary,
    mediaTypes: [.image]
) { [weak self] result, picker in
    switch result {
    case .success(let media):
        if let image = media.originalImage {
            self?.profileImageView.image = image
        }
    case .failure(let error):
        self?.showError(error.localizedDescription)
    }
    picker.dismiss(animated: true)
}

imagePicker.present(from: self)

UIAlertController

Tạo và present alert trở nên ngắn gọn:

let alert = UIAlertController(
    title: "Xác nhận xóa",
    message: "Bạn có chắc muốn xóa mục này?",
    preferredStyle: .alert
)

alert.addAction(title: "Xóa", style: .destructive) { _ in
    self.deleteItem()
}

alert.addAction(title: "Hủy", style: .cancel)

alert.present(from: self)

Cài đặt vào dự án

Swift Package Manager

Trong Xcode, chọn File → Add Packages... và nhập repository URL.

CocoaPods

Thêm vào Podfile:

pod 'Closures'

Carthage

Thêm vào Cartfile:

github "vhesener/Closures"

Manual

Kéo thả folder Sources vào project Xcode của bạn.

Lưu ý khi sử dụng

  • Luôn sử dụng [weak self] trong closure để tránh retain cycle
  • Thư viện tương thích từ iOS 8.0 trở lên
  • Có thể kết hợp với delegation truyền thống trong cùng một project

Giấy phép

Dự án được phát hành dưới MIT License, cho phép sử dụng tự do trong cả dự án thương mại và cá nhân.

Đăng vào ngày 29 tháng 9 lúc 16:20