Skip to content

Commit

Permalink
Merge pull request #78 from lightsprint09/data-modifier-agregator
Browse files Browse the repository at this point in the history
JoinedDataProvider
  • Loading branch information
lightsprint09 authored Jan 4, 2019
2 parents 6dc6123 + 4278b86 commit dd8d1a6
Show file tree
Hide file tree
Showing 13 changed files with 587 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
matrix:
include:
- os: osx
osx_image: xcode9.3
osx_image: xcode10.1
language: objective-c
skip-cleanup: true
env: "macOS"
Expand Down
5 changes: 3 additions & 2 deletions Demo/Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
TargetAttributes = {
C688F5BC1FFF6137000A4B06 = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1010;
ProvisioningStyle = Automatic;
};
};
Expand Down Expand Up @@ -323,7 +324,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.dbsystel.Demo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
Expand All @@ -337,7 +338,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.dbsystel.Demo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
Expand Down
2 changes: 1 addition & 1 deletion Demo/Demo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
Expand Down
2 changes: 1 addition & 1 deletion Source/Cells/ReusableViewType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
/// Defines of what type a Reusable view is
///
/// - SeeAlso: `ReusableViewConfiguring`
public enum ReusableViewType {
public enum ReusableViewType: Equatable {
/// For example a table view or collection view cell
case cell
/// A supplementaryView in a collection view
Expand Down
22 changes: 11 additions & 11 deletions Source/ChangeAnimator/TableViewChangesAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,18 @@
private let configuration: Configuration

public struct Configuration {
let insert: UITableViewRowAnimation
let update: UITableViewRowAnimation
let move: UITableViewRowAnimation
let delete: UITableViewRowAnimation
let insertSection: UITableViewRowAnimation
let updateSection: UITableViewRowAnimation
let deleteSection: UITableViewRowAnimation
let insert: UITableView.RowAnimation
let update: UITableView.RowAnimation
let move: UITableView.RowAnimation
let delete: UITableView.RowAnimation
let insertSection: UITableView.RowAnimation
let updateSection: UITableView.RowAnimation
let deleteSection: UITableView.RowAnimation

public init(insert: UITableViewRowAnimation = .automatic, update: UITableViewRowAnimation = .automatic,
move: UITableViewRowAnimation = .automatic, delete: UITableViewRowAnimation = .automatic,
insertSection: UITableViewRowAnimation = .automatic, updateSection: UITableViewRowAnimation = .automatic,
deleteSection: UITableViewRowAnimation = .automatic) {
public init(insert: UITableView.RowAnimation = .automatic, update: UITableView.RowAnimation = .automatic,
move: UITableView.RowAnimation = .automatic, delete: UITableView.RowAnimation = .automatic,
insertSection: UITableView.RowAnimation = .automatic, updateSection: UITableView.RowAnimation = .automatic,
deleteSection: UITableView.RowAnimation = .automatic) {
self.insert = insert
self.update = update
self.move = move
Expand Down
10 changes: 5 additions & 5 deletions Source/DataProvider/DataProvider/CollectionDataProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public extension CollectionDataProvider {
/// Return the number of sections.
///
/// - Returns: the number of sections.
public func numberOfSections() -> Int {
func numberOfSections() -> Int {
return content.count
}
}
Expand All @@ -49,28 +49,28 @@ public extension CollectionDataProvider where Container.Index == Int,
///
/// - Parameter indexPath: the index path to get the object for.
/// - Returns: the object at the given index path.
public func object(at indexPath: IndexPath) -> Element {
func object(at indexPath: IndexPath) -> Element {
return content[indexPath.section][indexPath.item]
}

/// Returns the number of items in a given section.
///
/// - Parameter section: the section.
/// - Returns: number of items in the given section.
public func numberOfItems(inSection section: Int) -> Int {
func numberOfItems(inSection section: Int) -> Int {
return content[section].count
}
}

extension CollectionDataProvider where Element: Equatable {
public extension CollectionDataProvider where Element: Equatable {

/**
Returns the indexPath for a given object.

- parameter object: the object to find the indexPath for.
- return: the indexPath of the object, if available.
*/
public func indexPath(for object: Element) -> IndexPath? {
func indexPath(for object: Element) -> IndexPath? {
for section in 0..<numberOfSections() {
for item in 0..<numberOfItems(inSection: section) {
let indexPath = IndexPath(item: item, section: section)
Expand Down
129 changes: 129 additions & 0 deletions Source/DataProvider/DataProvider/JoinedDataProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//
// Copyright (C) DB Systel GmbH.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//

import Foundation

/// Takes multiple data provider and aggregates them into a single on.
public final class JoinedDataProvider<Element>: DataProvider {

/// Creates an instance of `JoinedDataProvider` by aggregating all given data providers
///
/// - Parameter dataProviders: the data providers to aggregate.
public init<D: DataProvider>(dataProviders: [D]) where D.Element == Element {
self.dataProviders = dataProviders.map { AnyDataProvider($0) }
self.observers = dataProviders.enumerated().map { [unowned self] section, dataProvider in
dataProvider.observable.addObserver(observer: { self.translate(change: $0, section: section) })
}
}

deinit {
dataProviders.enumerated().forEach { index, dataProvider in
dataProvider.observable.removeObserver(observer: observers[index])
}
}

/// An observable where one can subscribe to changes of data provider.
public var observable: DataProviderObservable { return innerObaseravle }

/// Returns an object for a given index path.
///
/// - Parameter indexPath: the index path to get the object for.
/// - Returns: the object at the given index path.
public func object(at indexPath: IndexPath) -> Element {
let (dataProvider, realSection) = dataProviderAndIndexOffset(ofSection: indexPath.section)
let realIndexPath = IndexPath(row: indexPath.row, section: realSection)

return dataProvider.object(at: realIndexPath)
}

/// Returns the number of items in a given section.
///
/// - Parameter section: the section.
/// - Returns: number of items in the given section.
public func numberOfItems(inSection section: Int) -> Int {
let (dataProvider, realSection) = dataProviderAndIndexOffset(ofSection: section)
return dataProvider.numberOfItems(inSection: realSection)
}

// Return the number of sections.
///
/// - Returns: the number of sections.
public func numberOfSections() -> Int {
return dataProviders
.map { $0.numberOfSections() }
.reduce(0, +)
}

private func dataProviderAndIndexOffset(ofSection section: Int) -> (AnyDataProvider<Element>, Int) {
var sectionI = 0
for dataProvider in dataProviders {
if section >= sectionI && section < sectionI + dataProvider.numberOfSections() {
return (dataProvider, section - sectionI)
}
sectionI += dataProvider.numberOfSections()
}

fatalError("Invalid index")
}

private func translate(change: DataProviderChange, section: Int) {
switch change {
case .unknown:
innerObaseravle.send(updates: change)
case .changes(let changes):
let sectionOffset = dataProviders[0..<section].map { $0.numberOfSections() }.reduce(0, +)
innerObaseravle.send(updates: .changes(translate(changes: changes, sectionOffset: sectionOffset)))
case .viewUnrelatedChanges(let changes):
let sectionOffset = dataProviders[0..<section].map { $0.numberOfSections() }.reduce(0, +)
innerObaseravle.send(updates: .viewUnrelatedChanges(translate(changes: changes, sectionOffset: sectionOffset)))
}
}

private func translate(changes: [DataProviderChange.Change], sectionOffset: Int) -> [DataProviderChange.Change] {
return changes.map { change in
switch change {
case .update(let indexPath):
return .update(IndexPath(row: indexPath.row, section: sectionOffset + indexPath.section))
case .delete(let indexPath):
return .delete(IndexPath(row: indexPath.row, section: sectionOffset + indexPath.section))
case .insert(let indexPath):
return .insert(IndexPath(row: indexPath.row, section: sectionOffset + indexPath.section))
case .move(let sourceIndexPath, let destinationIndexPath):
return .move(IndexPath(row: sourceIndexPath.row, section: sectionOffset + sourceIndexPath.section),
IndexPath(row: destinationIndexPath.row, section: sectionOffset + destinationIndexPath.section))
case .insertSection(let insertedSection):
return .insertSection(sectionOffset + insertedSection)
case .updateSection(let updatedSection):
return .updateSection(sectionOffset + updatedSection)
case .deleteSection(let deletedSection):
return .deleteSection(sectionOffset + deletedSection)
case .moveSection(let sourceSection, let destinationSource):
return .moveSection(sectionOffset + sourceSection, sectionOffset + destinationSource)
}
}
}

private let dataProviders: [AnyDataProvider<Element>]
private let innerObaseravle = DefaultDataProviderObservable()
private var observers: [NSObjectProtocol]!

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// Copyright (C) DB Systel GmbH.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//

import Foundation

/// Takes a data provider and transforms it with a given closure into a new data provider with a different type.
///
/// - Note: Transformation will only execute when an object is requested by calling `object(at:)`.
public final class LazyTransformationDataProvider<TargetType>: DataProvider {

// MARK: - Init

public init<D: DataProvider>(dataProvider: D, transform: @escaping (D.Element) -> TargetType) {
self.numberOfSection = { dataProvider.numberOfSections() }
self.numberOfElements = { dataProvider.numberOfItems(inSection: $0) }
self.objectAtIndexPath = { transform(dataProvider.object(at: $0)) }
self.observable = dataProvider.observable
}

// MARK: - Protocol DataProvider

/// An observable where one can subscribe to changes of data provider.
public var observable: DataProviderObservable

/// Returns an object for a given index path.
///
/// - Parameter indexPath: the index path to get the object for.
/// - Returns: the object at the given index path.
public func object(at indexPath: IndexPath) -> TargetType {
return objectAtIndexPath(indexPath)
}

/// Returns the number of items in a given section.
///
/// - Parameter section: the section.
/// - Returns: number of items in the given section.
public func numberOfItems(inSection section: Int) -> Int {
return numberOfElements(section)
}

/// Return the number of sections.
///
/// - Returns: the number of sections.
public func numberOfSections() -> Int {
return numberOfSection()
}

// MARK: - Private

private let objectAtIndexPath: (_ indexPath: IndexPath) -> TargetType
private let numberOfSection: () -> Int
private let numberOfElements: (_ inSection: Int) -> Int

}

extension DataProvider {

/// Lazy transforms the content with a given closure into a new data provider.
///
/// - Parameter transform: the closure which transforms the element into the target value
/// - Returns: a `LazyTransformationDataProvider` which provides newly transformed objects.
///
/// - SeeAlso: `LazyTransformationDataProvider`
public func lazyTransform<TargetType>(_ transform: @escaping (Element) -> TargetType) ->
LazyTransformationDataProvider<TargetType> {
return LazyTransformationDataProvider(dataProvider: self, transform: transform)
}
}
2 changes: 1 addition & 1 deletion Source/DataSource/TableViewDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
}

/// :nodoc:
public func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle,
public func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle,
forRowAt indexPath: IndexPath) {
guard let dataModificator = dataModificator else {
return
Expand Down
Loading

0 comments on commit dd8d1a6

Please sign in to comment.