Skip to content

Commit

Permalink
Add support for new iOS 16 / tvOS 16 SDK delegate methods. (#82)
Browse files Browse the repository at this point in the history
* Add support for new iOS 16 / tvOS 16 SDK delegate methods. Soft-deprecate old extensions for events on `DTTableViewManager`.

* Fix build for Mac Catalyst on Xcode 14.0, while supporting future Xcode 14.1

* Rewrite conditionals to properly include code for MacCatalyst.

* Enable testing for Mac Catalyst.

* Fix unit tests warnings on Mac Catalyst.
  • Loading branch information
DenTelezhkin authored Oct 2, 2022
1 parent f27e88b commit 18509e1
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 34 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
DEVELOPER_DIR: /Applications/Xcode_13.4.1.app/Contents/Developer
strategy:
matrix:
destination: ["test_ios15", "test_tvos15", "build_macos_catalyst"]
destination: ["test_ios15", "test_tvos15", "test_catalyst"]
steps:
- name: git checkout
uses: actions/checkout@v2
Expand All @@ -55,7 +55,7 @@ jobs:
- name: ${{ matrix.destination }}
run: bundle exec fastlane ${{ matrix.destination }}
- name: Generate code coverage
if: matrix.destination != 'build_macos_catalyst'
if: matrix.destination != 'test_catalyst'
run: bundle exec fastlane generate_code_coverage
- name: Codecov
uses: codecov/codecov-action@v3
Expand All @@ -67,7 +67,7 @@ jobs:
DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer
strategy:
matrix:
destination: ["test_ios16", "test_tvos16", "build_macos_catalyst"]
destination: ["test_ios16", "test_tvos16", "test_catalyst"]
steps:
- name: git checkout
uses: actions/checkout@v2
Expand All @@ -79,7 +79,7 @@ jobs:
- name: ${{ matrix.destination }}
run: bundle exec fastlane ${{ matrix.destination }}
- name: Generate code coverage
if: matrix.destination != 'build_macos_catalyst'
if: matrix.destination != 'test_catalyst'
run: bundle exec fastlane generate_code_coverage
- name: Codecov
uses: codecov/codecov-action@v2
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.

### Added

* Support for `UITableViewDelegate.tableView(_:canPerformPrimaryActionForRowAt:)` and `UITableViewDelegate.tableView(_:performPrimaryActionForRowAt:)` delegate methods on iOS 16 and tvOS 16.
* Support for events, wrapping `UITableViewDataSourcePrefetching` protocol.

```swift
Expand All @@ -17,6 +18,25 @@ manager.register(PostCell.self) { mapping in

> Please note, that while datasource methods are called once per array of indexPaths, events for models will be called individually, so single model (and indexPath) is passed to each event. Theoretically, this should make prefetching and cancellation easier, since you no longer need to walk through array and find all data models, you can operate on a single data model at a time.

### Deprecated

* Cell / View events, registered with `DTTableViewManager` are soft-deprecated. Please use events in mapping instead:

Deprecated:
```swift
manager.register(PostCell.self)
manager.didSelect(PostCell.self) { postCell, post, indexPath in }
```
Recommended:
```swift
manager.register(PostCell.self) { mapping in
mapping.didSelect { postCell, post, indexPath in }
}
```
> While previously main benefits for second syntax were mostly syntactic, now with support for SwiftUI it will be hard to actually specialize hosting cells (and might be impossible when iOS 16 hosting configuration is supported), so only second syntax will work for all kinds of cells, and first syntax can only work for non-SwiftUI cells.
> New delegate methods for UITableView (starting with iOS 16 / tvO 16 SDK) will be added only as extension to mapping protocols, not DTTableViewManager itself.
## [11.0.0-beta.1](https://github.com/DenTelezhkin/DTTableViewManager/releases/tag/11.0.0-beta.1)

### **Introducing support for SwiftUI!**
Expand Down
6 changes: 4 additions & 2 deletions DTTableViewManager.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,7 @@
APPLICATION_EXTENSION_API_ONLY = NO;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = "Supporting files/Tests.plist";
Expand All @@ -1101,7 +1102,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = "MLSDev.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
};
name = Debug;
};
Expand All @@ -1112,6 +1113,7 @@
APPLICATION_EXTENSION_API_ONLY = NO;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
ENABLE_NS_ASSERTIONS = NO;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
Expand All @@ -1123,7 +1125,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = "MLSDev.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
};
name = Release;
};
Expand Down
18 changes: 18 additions & 0 deletions Sources/DTTableViewManager/DTTableViewDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -502,4 +502,22 @@ open class DTTableViewDelegate : DTTableViewDelegateWrapper, UITableViewDelegate
return tableView.selectionFollowsFocus
}
#endif

#if swift(>=5.7) || (os(macOS) && swift(>=5.7.1)) // Xcode 14.0 AND macCatalyst on Xcode 14.1 (which will have swift> 5.7.1)
@available(iOS 16, tvOS 16, *)
/// Implementation for `UITableViewDelegate` protocol
public func tableView(_ tableView: UITableView, canPerformPrimaryActionForRowAt indexPath: IndexPath) -> Bool {
if let canPerform = performCellReaction(.canPerformActionForRowAtIndexPath, location: indexPath, provideCell: true) as? Bool {
return canPerform
}
return (delegate as? UITableViewDelegate)?.tableView?(tableView, canPerformPrimaryActionForRowAt: indexPath) ?? false
}

@available(iOS 16, tvOS 16, *)
/// Implementation for `UITableViewDelegate` protocol
public func tableView(_ tableView: UITableView, performPrimaryActionForRowAt indexPath: IndexPath) {
_ = performCellReaction(.performPrimaryActionForRowAtIndexPath, location: indexPath, provideCell: true)
(delegate as? UITableViewDelegate)?.tableView?(tableView, performPrimaryActionForRowAt: indexPath)
}
#endif
}
29 changes: 28 additions & 1 deletion Sources/DTTableViewManager/DTTableViewManager+Delegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,21 @@ import Foundation
import UIKit
import DTModelStorage

/// Extension for registering UITableViewDelegate events
/// Extension for registering UITableViewDelegate events. Please note that cell / view methods in this extension are soft-deprecated, and it's recommended to migrate to methods extending `CellViewModelMappingProtocolGeneric` and `SupplementaryViewModelMappingProtocolGeneric`:
///
/// Deprecated:
/// ```swift
/// manager.register(PostCell.self)
/// manager.didSelect(PostCell.self) { postCell, post, indexPath in }
/// ```
/// Recommended:
/// ```swift
/// manager.register(PostCell.self) { mapping in
/// mapping.didSelect { postCell, post, indexPath in }
/// }
/// ```
/// While previously main benefits for second syntax were mostly syntactic, now with support for SwiftUI it will be hard to actually specialize hosting cells, so only second syntax will work for all kinds of cells, and first syntax can only work for non-SwiftUI cells.
/// New delegate methods for UITableView (starting with iOS 16 / tvO 16 SDK) will be added only as extension to mapping protocols, not DTTableViewManager itself.
public extension DTTableViewManager {
/// Registers `closure` to be executed, when `UITableViewDelegate.tableView(_:didSelectRowAt:)` method is called for `cellClass`.
func didSelect<Cell:ModelTransfer>(_ cellClass: Cell.Type, _ closure: @escaping (Cell, Cell.ModelType, IndexPath) -> Void) where Cell:UITableViewCell
Expand Down Expand Up @@ -524,6 +538,19 @@ public extension CellViewModelMappingProtocolGeneric {
closure))
}
#endif


#if swift(>=5.7) || (os(macOS) && swift(>=5.7.1)) // Xcode 14.0 AND macCatalyst on Xcode 14.1
@available(iOS 16, tvOS 16, *)
func canPerformPrimaryAction(_ closure: @escaping (Cell, Model, IndexPath) -> Bool) {
reactions.append(EventReaction(viewType: Cell.self, modelType: Model.self, signature: EventMethodSignature.canPerformActionForRowAtIndexPath.rawValue, closure))
}

@available(iOS 16, tvOS 16, *)
func performPrimaryAction(_ closure: @escaping (Cell, Model, IndexPath) -> Void) {
reactions.append(EventReaction(viewType: Cell.self, modelType: Model.self, signature: EventMethodSignature.performPrimaryActionForRowAtIndexPath.rawValue, closure))
}
#endif
}

/// Extension registering events for UITableViewDelegate
Expand Down
2 changes: 2 additions & 0 deletions Sources/DTTableViewManager/DTTableViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ internal enum EventMethodSignature: String {
case previewForHighlightingContextMenu = "tableView:previewForHighlightingContextMenuWithConfiguration:"
case previewForDismissingContextMenu = "tableView:previewForDismissingContextMenuWithConfiguration:"
case selectionFollowsFocusForRowAtIndexPath = "tableView:selectionFollowsFocusForRowAtIndexPath:"
case canPerformPrimaryActionForRowAtIndexPath = "tableView:canPerformPrimaryActionForRowAtIndexPath:"
case performPrimaryActionForRowAtIndexPath = "tableView:performPrimaryActionForRowAtIndexPath:"

/// UITableViewDragDelegate
case itemsForBeginningDragSession = "tableView:itemsForBeginningDragSession:atIndexPath:"
Expand Down
4 changes: 2 additions & 2 deletions Sources/Tests/DatasourceTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ class DatasourceTestCase: BaseTestCase {
let model: Int?? = nil
let anomaly = DTTableViewManagerAnomaly.nilCellModel(indexPath(0, 0))
controller.manager.anomalyHandler.anomalyAction = exp.expect(anomaly: anomaly)
controller.manager.memoryStorage.addItem(model)
controller.manager.memoryStorage.setSection(SectionModel(items: [model as Any]), forSection: 0)
let _ = controller.manager.tableDataSource?.tableView(controller.tableView, cellForRowAt: indexPath(0, 0))

waitForExpectations(timeout: 0.1)
Expand Down Expand Up @@ -350,7 +350,7 @@ class DatasourceTestCase: BaseTestCase {
let exp = expectation(description: "No cell mappings found for model")
let anomaly = DTTableViewManagerAnomaly.noCellMappingFound(modelDescription: "3", indexPath: indexPath(0, 0))
controller.manager.anomalyHandler.anomalyAction = exp.expect(anomaly: anomaly)
controller.manager.memoryStorage.addItem("3")
controller.manager.memoryStorage.setSection(SectionModel(items: ["3"]), forSection: 0)
let _ = controller.manager.tableDataSource?.tableView(controller.tableView, cellForRowAt: indexPath(0, 0))
waitForExpectations(timeout: 0.1)

Expand Down
Loading

0 comments on commit 18509e1

Please sign in to comment.