Skip to content

KopievDev/CombineCocoa

Repository files navigation

CombineCocoa

CombineCocoa simplifies the connection between UIKit and Combine, allowing seamless binding of UI components to reactive data streams.

Features

  • Bind UI elements like UICollectionView and UITableView to Published data streams.
  • Support for custom cell types using Combine.

Example 1: Binding UICollectionView to a Published property

@Published var items = [String]()
let collectionView = UICollectionView()
private var subscriptions = Set<AnyCancellable>()

collectionView.bind($items, cellType: SomeCell.self) { index, element, cell in
    cell.render(model: element)
}.store(in: &subscriptions)

Example 2: Complex cell binding with multiple cell types

enum CellType {
    case regular(DataModel)
    case header(String)
}

@Published var cells = [CellType]()
let collectionView = UICollectionView()
private var subscriptions = Set<AnyCancellable>()

collectionView.bind($cells) { collectionView, indexPath, items in
    switch items {
    case .regular(let data):
        return collectionView.dequeueCell(RegularCell.self, indexPath) { cell in
            cell.configure(with: data)
        }
    case .header(let title):
        return collectionView.dequeueCell(HeaderCell.self, indexPath) { cell in
            cell.setTitle(title)
        }
    }
}.store(in: &subscriptions)

Example 3: Binding UITableView

@Published var tableItems = [String]()
let tableView = UITableView()
private var subscriptions = Set<AnyCancellable>()

tableView.bind($tableItems, cellType: UITableViewCell.self) { index, item, cell in
    cell.textLabel?.text = item
}.store(in: &subscriptions)

Example 4: Using BaseCell with Subclasses

Base Cell Implementation:

open class BaseCell<ViewModel>: UITableViewCell {
    public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }

    public required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    open func commonInit() {
        selectionStyle = .none
    }

    open func render(viewModel: ViewModel) { }
}

Subclass Implementation:

final class UserCell: BaseCell<UserViewModel> {
    private let nameLabel = UILabel()

    override func commonInit() {
        super.commonInit()
        contentView.addSubview(nameLabel)
    }

    override func render(viewModel: UserViewModel) {
        nameLabel.text = viewModel.name
    }
}

final class ProductCell: BaseCell<ProductViewModel> {
    private let productLabel = UILabel()

    override func commonInit() {
        super.commonInit()
        contentView.addSubview(productLabel)
    }

    override func render(viewModel: ProductViewModel) {
        productLabel.text = viewModel.title
    }
}

Using Subclasses in a Table:

@Published var users: [UserViewModel] = []
let tableView = UITableView()
private var subscriptions = Set<AnyCancellable>()

tableView.bind($users, cellType: UserCell.self) { index, user, cell in
    cell.render(viewModel: user)
}.store(in: &subscriptions)

Installation

Swift Package Manager

Add this to your Package.swift file:

dependencies: [
    .package(url: "https://github.com/KopievDev/CombineCocoa", from: "1.0.0")
]