Swiftpack.co - Package - DenTelezhkin/DTTableViewManager

CI codecov.io CocoaPod platform CocoaPod version Swift Package Manager compatible Packagist

DTTableViewManager

Features

  • ☑ Powerful mapping system between data models and cells, headers and footers
  • ☑ Automatic datasource and interface synchronization.
  • ☑ Flexible Memory/CoreData/Realm/diffable datasource storage options
  • ☑ Powerful compile-time safe events system, that covers all of UITableView delegate methods
  • ☑ Views created from code, XIB, or storyboard, automatic registration and dequeue
  • ☑ Can be used with UITableViewController, or UIViewController with UITableView
  • ☑ Unified syntax with DTCollectionViewManager
  • Complete documentation
  • API Reference

Requirements

  • Xcode 12+
  • iOS 11.0+ / tvOS 11.0+ / macCatalyst 13.0+
  • Swift 5.3+

If you need Xcode 11 support or Swift 4...Swift 5.2, or iOS 8...iOS 10 support, you can use 7.x releases.

Installation

Swift Package Manager

Add package into Xcode Project settings -> Swift Packages

CocoaPods:

pod 'DTTableViewManager', '~> 8.0.0'

Quick start

Let's say you have an array of Posts you want to display in UITableView. To quickly show them using DTTableViewManager, here's what you need to do:

  1. Create UITableViewCell subclass, let's say PostCell and adopt ModelTransfer protocol:
class PostCell : UITableViewCell, ModelTransfer {
    func update(with model: Post) {
        // Fill your cell with actual data
    }
}
  1. In your view controller:
class PostsViewController: UITableViewController, DTTableViewManageable {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Register PostCell to be used with this controller's table view
        manager.register(PostCell.self)

        // Populate datasource
        manager.memoryStorage.setItems(posts)
    }
}    

Make sure your UITableView outlet is wired to your class (or use UITableViewController subclass). If you have a PostCell.xib file, it will be automatically used for dequeueing PostCell.

  1. That's it! It's that easy!

Of course, cool stuff does not stop there, framework supports all datasource and delegate methods as closures, conditional mappings and much much more! Choose what interests you in the next section of readme.

Burning questions

Starter pack
Advanced

Sample code and documentation

Thanks

  • Alexey Belkevich for providing initial implementation of CellFactory.
  • Michael Fey for providing insight into NSFetchedResultsController updates done right.
  • Nickolay Sheika for great feedback, that helped shaping 3.0 release and future direction of the library.
  • Artem Antihevich for great discussions about Swift generics and type capturing.

Github

link
Stars: 421

Dependencies

Used By

Total: 0

Releases

8.0.0 -

8.0.0-beta.1 -

Added

  • Cell and supplementary view events are now available inside mapping closure directly, for example:
// Previous releases
manager.register(PostCell.self)
manager.didSelect(PostCell.self) { cell, model, indexPath in
    // React to selection
}

// New
manager.register(PostCell.self) { mapping in
    mapping.didSelect { cell, model, indexPath in

    }
}

Those events are now tied to ViewModelMapping instance, which means, that events, registered this way, will only trigger, if mapping condition of current mapping applies. For example:

manager.register(PostCell.self) { mapping in
    mapping.condition = .section(0)
    mapping.didSelect { cell, model, indexPath in  
        // This closure will only get called, when user selects cell in the first section
    }
}
manager.register(PostCell.self) { mapping in
    mapping.condition = .section(1)
    mapping.didSelect { cell, model, indexPath in  
        // This closure will only get called, when user selects cell in the second section
    }
}

Please note, that headers and footers only support mapping-style event registration, if they inherit from UITableViewHeaderFooterView.

  • TableViewConfiguration semanticHeaderHeight and semanticFooterHeight, that specify whether DTTableViewManager should deploy custom logic in tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) and tableView(_ tableView: UITableView, heightForFooterInSection section: Int). This logic includes checking whether header and footer models exist in storage, returning UITableView.automaticDimension for sections, whose header and footer models are Strings (for table section titles), as well as returning minimal height for cases where data model is not there(which happens to be different for UITableView.Style.plain and UITableView.Style.grouped). Those properties default to true, but if you want to use self-sizing table view sections headers or footers, which may improve perfomance, consider turning those off:
manager.configuration.semanticHeaderHeight = false
manager.configuration.semanticFooterHeight = false

Please note, that even when those properties are set to false, corresponding UITableViewDelegate methods will still be called in two cases:

  1. Your DTTableViewManageable instance implements them
  2. You register a heightForHeader(withItem:_:) or heightForFooter(withItem:_:) closures on DTTableViewManager instance.

Breaking

This release requires Swift 5.3. Minimum iOS / tvOS deployment targets are unchanged (iOS 11, tvOS 11).

Some context: this release heavily relies on where clauses on contextually generic declarations, that are only available in Swift 5.3 - SE-0267.

  • ViewModelMapping is now a generic class, that captures view and model information(ViewModelMapping<T,U>).

Fixed

  • indentationLevelForCell closure now correctly returns Int instead of CGFloat.
  • Several event API's have been improved to allow returning nil for methods, that accept nil as a valid value: contextMenuConfiguration, previewForHighlightingContextMenu, previewForDismissingContextMenu.

Changed

  • Generic placeholders for cell/model/view methods have been improved for better readability.

Deprecated

  • Several cell/header/footer/supplementary view registration methods have been deprecated to unify registration logic. Please use register(_:mapping:handler:), registerHeader(_:mapping:handler:), registerFooter(_:mapping:handler:) as a replacements for all of those methods. For more information on those changes, please read migration guide.
  • All non-deprecated registration methods now have an additional handler closure, that allows to configure cells/headers/footers that are dequeued from UITableView. This is a direct replacement for configure(_:_:, configureHeader(_:_:), configureFooter(_:_:) , that are all now deprecated. Please note, that handler closure is called before DTModelTransfer.update(with:) method.
  • DTTableViewManager.configureEvents(for:_:), it's functionality has become unnecessary since mapping closure of cell/header/footer registration now captures both cell and model type information for such events.
  • DTTableViewManager.configureDiffableDataSource(modelProvider:) for non-hashable data models. Please use configureDiffableDataSource method for models, that are Hashable. From Apple's documentation: If you’re working in a Swift codebase, always use UITableViewDiffableDataSource instead.
  • TableViewUpdater.usesLegacyTableViewUpdateMethods property.

7.2.0 -

7.1.0 -

Changed

  • It's not longer necessary to import DTModelStorage framework to use it's API's. import DTTableViewManager now implicitly exports DTModelStorage.

7.0.0 -

  • willCommitMenuWithAnimator method has been made unavailable for Xcode 11.2, because UITableViewDelegate method it used has been removed from UIKit on Xcode 11.2.

7.0.0-beta.2 -

  • Added support for Xcode versions, that are older than Xcode 11.

7.0.0-beta.1 -

This is a major release with some breaking changes, please read DTTableViewManager 7.0 Migration Guide

Added

  • configureDiffableDataSource(modelProvider:) method to enable UITableViewDiffableDataSource with DTTableViewManager.
  • Ability for DTTableViewManageable to implement tableView(_:viewForHeaderInSection:) and tableView(_:viewForFooterInSection:) to return view directly without going through storages.
  • minimalHeaderHeightForTableView and minimalFooterHeightForTableView properties for TableViewConfiguration, that allows configuring height for section headers and footers that need to be hidden.
  • Ability to customize bundle, from which xib files are loaded from by setting bundle property on ViewModelMapping in mappingBlock. As before, bundle defaults to Bundle(for: ViewClass.self).
  • DTTableViewManager.supplementaryStorage getter, that conditionally casts current storage to SupplementaryStorage protocol.

New method wrappers for iOS 13 API

  • shouldBeginMultipleSelectionInteraction
  • didBeginMultipleSelectionInteraction
  • didEndMultipleSelectionInteraction
  • contextMenuConfiguration(for:)
  • previewForHighlightingContextMenu
  • previewForDismissingContextMenu
  • willCommitMenuWithAnimator

Changed

  • If tableView section does not contain any items, and TableViewConfiguration.display<Header/Footer>OnEmptySection property is set to false, DTTableViewManager no longer asks for header footer height explicitly and returns TableViewConfiguration.minimal<Header/Footer>HeightForTableView.
  • Anomaly event verification now allows subclasses to prevent false-positives.
  • animateChangesOffScreen property on TableViewUpdater that allows to turn off animated updates for UITableView when it is not on screen.

Removed

  • Usage of previously deprecated and now removed from DTModelStorage ViewModelMappingCustomizing protocol.

Breaking

DTModelStorage header, footer and supplementary model handling has been largely restructured to be a single closure-based API. Read more about changes in DTModelStorage changelog. As a result of those changes, several breaking changes in DTTableViewManager include:

  • SupplementaryAccessible extension with tableHeaderModel and tableFooterModel properties has been removed.
  • Because headers/footers are now a closure based API, setSectionHeaderModels and setSectionFooterModels do not create sections by default, and do not call tableView.reloadData.
  • If a storage does not contain any sections, even if configuration.displayHeaderOnEmptySections or configuration.displayFooterOnEmptySections is set, headers and footers will not be displayed, since there are no sections, which is different from present sections, that contain 0 items. For example, If you need to show a header or footer in empty section using MemoryStorage, you can call memoryStorage.setItems([Int](), forSectionAt: emptySectionIndex), and now with empty section header and footer can be displayed.

Other breaking changes:

  • tableViewUpdater will contain nil if DTTableViewManager is configured to work with UITableViewDiffableDataSource.
  • DTTableViewOptionalManageable protocol was removed and replaced by optionalTableView property on DTTableViewManageable protocol. One of tableView/optionalTableView properties must be implemented by DTTableViewManageable instance to work with DTTableViewManager.

Deprecated

Following methods have been deprecated due to their delegate methods being deprecated in iOS 13:

  • editActions(for:)
  • shouldShowMenuForItemAt
  • canPerformAction
  • performAction

6.6.0 -

  • Added support for Swift Package Manager in Xcode 11

6.5.0 -

Added

  • Convenience constructor for DTTableViewManager object: init(storage:) that allows to create it's instance without initializing MemoryStorage.
  • Static variable defaultStorage on DTTableViewManager that allows to configure which Storage class is used by default.
  • Documentation
  • Support for Xcode 10.2 and Swift 5

Removed

  • Support for Xcode 9 and Swift 3

6.4.0 -

Dependency changelog -> DTModelStorage 7.2.0 and higher

Added

  • Example of auto-diffing capability and animations when using SingleSectionStorage.
  • Support for Swift 4.2 and Xcode 10.

Changed

  • Reduced severity comment for nilHeaderModel and nilFooterModel anomalies, since in some cases it's actually a desired behavior.

6.3.0 -

Added

  • Anomaly detecting system for various errors in DTTableViewManager. Read more about it in Anomaly Handler Readme section. Anomaly handler system requires Swift 4.1 and higher.
  • Support for Swift 4.2 in Xcode 10 beta 1.

Changed

  • Calling startManaging(withDelegate:_) method is no longer required.

Breaking

  • viewFactoryErrorHandler deprecated property on DTTableViewManager was removed. All previously reported errors and warnings are now a part of anomaly detecting system.

6.2.0 -

Breaking

  • editingStyle(for:_,_:) method was replaced with editingStyle(forItem:_,:_) method, that accepts model and indexPath closure, without cell. Reason for that is that UITableView may call this method when cell is not actually on screen, in which case this event would not fire, and current editingStyle of the cell would be lost.

6.1.1 -

  • Updates for Xcode 9.3 and Swift 4.1

6.1.0 -

6.1.0-beta.1 -

  • Implemented new system for deferring datasource updates until performBatchUpdates block. This system is intended to fight crash, that might happen when performBatchUpdates method is called after UITableView.reloadData method(for example after calling memoryStorage.setItems, and then immediately memoryStorage.addItems). This issue is detailed in https://github.com/DenHeadless/DTCollectionViewManager/issues/27 and https://github.com/DenHeadless/DTCollectionViewManager/issues/23. This crash can also happen, if iOS 11 API UITableView.performBatchUpdates is used. This system is turned on by default. If, for some reason, you want to disable it and have old behavior, call:
manager.memoryStorage.defersDatasourceUpdates = false
  • TableViewUpdater now uses iOS 11 performBatchUpdates API, if this API is available. This API will work properly on MemoryStorage only if defersDatasourceUpdates is set to true - which is default. However, if for some reason you need to use legacy methods beginUpdates, endUpdates, you can enable them like so:
manager.tableViewUpdater?.usesLegacyTableViewUpdateMethods = true

Please note, though, that new default behavior is recommended, because it is more stable and works the same on both UITableView and UICollectionView.

  • tableViewUpdater property on DTTableViewManager is now of TableViewUpdater type instead of opaque StorageUpdating type. This should ease use of this object and prevent type unneccessary type casts.

6.0.0 -

  • Updated to Xcode 9.1 / Swift 4.0.2

6.0.0-beta.3 -

  • Makes DTTableViewManager property weak instead of unowned to prevent iOS 10-specific memory issues/crashes.

6.0.0-beta.2 -

  • Build with Xcode 9.0 final release.
  • Fixed partial-availability warnings.

6.0.0-beta.1 -

This is a major release with some breaking changes, please read DTTableViewManager 6.0 Migration Guide

  • Added updateVisibleCells(_:) method, that allows updating cell data for visible cells with callback on each cell. This is more efficient than calling reloadData when number of elements in UITableView does not change, and only contents of items change.
  • Implement configureEvents(for:_:) method, that allows batching in several cell events to avoid using T.ModelType for events, that do not have cell created.
  • Added event for UITableViewDelegate tableView(_:targetIndexPathForMoveFromRowAt:toProposedIndexPath:
  • Added events for focus engine on iOS 9
  • Added events for iOS 11 UITableViewDelegate methods: tableView(_:leadingSwipeActionsConfigurationForRowAt:, tableView(_:trailingSwipeActionsConfigurationForRowAt:, tableView(_:shouldSpringLoadRowAt:withContext:
  • UITableViewDelegate and UITableViewDatasource implementations have been refactored from DTTableViewManager to DTTableViewDelegate and DTTableViewDataSource classes.
  • DTTableViewManager now allows registering mappings for specific sections, or mappings with any custom condition.
  • Added move(_:_:) method to allow setting up events, reacting to tableView:moveRowAt:to: method.

Breaking

  • Signature of move(_:_:) method has been changed to make it consistent with other events. Arguments received in closure are now: (destinationIndexPath: IndexPath, cell: T, model: T.ModelType, sourceIndexPath: IndexPath)
  • tableView(UITableView, moveRowAt: IndexPath, to: IndexPath) no longer automatically moves items, if current storage is MemoryStorage. Please use MemoryStorage convenience method moveItemWithoutAnimation(from:to:) to move items manually.

Deprecated

  • Error handling system of DTTableViewManager is deprecated and can be removed or replaced in future versions of the framework.

5.3.0 -

  • Use new events system from DTModelStorage, that allows events to be properly called for cells, that are created using ViewModelMappingCustomizing protocol.

5.2.0 -

New

  • Setting TableViewUpdater instance to tableViewUpdater property on DTTableViewManager now triggers didUpdateContent closure on TableViewUpdater.
  • Added sectionIndexTitles event to replace UITableViewDataSource.sectionIndexTitles(for:) method.
  • Added sectionForSectionIndexTitle event to replace UITableViewDataSource.tableView(_:sectionForSectionIndexTitle:at) method.

Bugfixes

  • All events that return Optional value now accept nil as a valid event result.
  • didDeselect(_:,_:) method now accepts closure without return type - since UITableViewDelegate does not have return type in that method.

5.1.0 -

Dependency changelog -> DTModelStorage 4.0.0 and higher

  • TableViewUpdater has been rewritten to use new StorageUpdate properties that track changes in order of their occurence.
  • TableViewUpdater reloadRowClosure and DTTableViewManager updateCellClosure now accept indexPath and model instead of just indexPath. This is done because update may happen after insertions and deletions and object that needs to be updated may exist on different indexPath.

5.0.0 -

No changes from previous beta.

5.0.0-beta.3 -

  • DTModelStorage dependency now requires Realm 2.0
  • UITableViewDelegate heightForHeaderInSection and heightForFooterInSection are now properly called on the delegate, if it implements it(thanks, @augmentedworks!).

5.0.0-beta.2 -

Added

  • DTTableViewOptionalManageable protocol, that is identical to DTTableViewManageable, but allows optional tableView property instead of implicitly unwrapped one.
  • Enabled RealmStorage from DTModelStorage dependency

5.0.0-beta.1 -

This is a major release, written in Swift 3. Read [Migration guide](Documentation/DTTableViewManager 5.0 migration guide.md) with descriptions of all features and changes.

Dependency changelog -> DTModelStorage 3.0.0 and higher

Added

  • New events system that covers almost all available UITableViewDelegate and UITableViewDataSource delegate methods.
  • New class - TableViewUpdater, that is calling all animation methods for UITableView when required by underlying storage.
  • updateCellClosure method on DTTableViewManager, that manually updates visible cell instead of calling tableView.reloadRowsAt(_:) method.
  • coreDataUpdater property on DTTableViewManager, that creates TableViewUpdater object, that follows Apple's guide for updating UITableView from NSFetchedResultsControllerDelegate events.
  • isManagingTableView property on DTTableViewManager
  • unregisterCellClass(_:), unregisterHeaderClass(_:), unregisterFooterClass(_:) methods to unregister mappings from DTTableViewManager and UITableView

Changed

  • Event system is migrated to new EventReaction class from DTModelStorage
  • Swift 3 API Design guidelines have been applied to all public API.
  • Section and row animations are now set on TableViewUpdater class instead of TableViewConfiguration

Removals

  • itemForVisibleCell, itemForCellClass:atIndexPath:, itemForHeaderClass:atSectionIndex:, itemForFooterClass:atSectionIndex: were removed - they were not particularly useful and can be replaced with much shorter Swift conditional typecasts.
  • registerCellClass:whenSelected method
  • All events methods with method pointer semantics. Please use block based methods instead.
  • dataBindingBehaviour property.
  • viewBundle property on DTTableViewManager. Bundle is not determined automatically based on view class.
  • DTTableViewContentUpdatable protocol. Use TableViewUpdater properties instead.

4.8.0 -

Changed

  • Support for building in both Swift 2.2 and Swift 2.3
  • Now all view registration methods use NSBundle(forClass:) constructor, instead of falling back on DTTableViewManager viewBundle property. This allows having cells from separate bundles or frameworks to be used with single DTTableViewManager instance.

Deprecations

  • viewBundle property on DTTableViewManager

3.3.0 [Objective-C] -

Changes

  • Always use NSBundle(forClass:) instead of NSBundle.mainBundle, thus allowing usage of table view cells, headers in footers from different bundles and framework - thanks @belkevich!

4.7.0 -

Dependency changelog -> DTModelStorage 2.6.0 and higher

4.6.0 -

Dependency changelog -> DTModelStorage 2.5 and higher

Breaking

  • Update to Swift 2.2. This release is not backwards compatible with Swift 2.1.

Changed

  • Require Only-App-Extension-Safe API is set to YES in framework targets.