diff --git a/.gitignore b/.gitignore index 4d469d9..8add01e 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,6 @@ xcuserdata/ ## Other *.moved-aside *.xcuserstate -*.xcworkspace ## Obj-C/Swift specific *.hmap diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c25a7ad --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "External/Nimble"] + path = External/Nimble + url = git://github.com/Quick/Nimble.git diff --git a/.swift-version b/.swift-version new file mode 100644 index 0000000..f398a20 --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +3.0 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 58bc0f8..16cb2c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode7.3 +osx_image: xcode8 script: sh build.sh after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/Assets/logo.png b/Assets/logo.png new file mode 100644 index 0000000..7912a8e Binary files /dev/null and b/Assets/logo.png differ diff --git a/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj b/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj index 49ec4c5..6a103f0 100644 --- a/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj +++ b/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj @@ -16,7 +16,13 @@ CC19AB271D7DAB7F00B8B196 /* PickerControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC19AB261D7DAB7F00B8B196 /* PickerControl.swift */; }; CC28B4091D7D060B00B7CA97 /* CustomHeaderFooterView.xib in Resources */ = {isa = PBXBuildFile; fileRef = CC28B4081D7D060B00B7CA97 /* CustomHeaderFooterView.xib */; }; CC28B40B1D7D064700B7CA97 /* CustomHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC28B40A1D7D064700B7CA97 /* CustomHeader.swift */; }; + CC2B23AE1D84DF4B0025CDF8 /* ActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC2B23AD1D84DF4B0025CDF8 /* ActionBar.swift */; }; CC5234581D730EF500F312E7 /* SelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC5234571D730EF500F312E7 /* SelectionViewController.swift */; }; + CCBD2FBC1D87344E00C61DCC /* ActionBarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBD2FBB1D87344E00C61DCC /* ActionBarManager.swift */; }; + CCBD2FC21D874BBA00C61DCC /* Rules.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBD2FBE1D874BBA00C61DCC /* Rules.swift */; }; + CCBD2FC31D874BBA00C61DCC /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBD2FBF1D874BBA00C61DCC /* Validation.swift */; }; + CCBD2FC41D874BBA00C61DCC /* ValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBD2FC01D874BBA00C61DCC /* ValidationError.swift */; }; + CCBD2FC51D874BBA00C61DCC /* Validator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBD2FC11D874BBA00C61DCC /* Validator.swift */; }; CCDDB6971D7DA9CB006C0F9F /* CustomItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDDB6951D7DA9CB006C0F9F /* CustomItem.swift */; }; CCDDB6981D7DA9CB006C0F9F /* TextFieldItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDDB6961D7DA9CB006C0F9F /* TextFieldItem.swift */; }; CCDDB69A1D7DA9DE006C0F9F /* TextFieldCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = CCDDB6991D7DA9DE006C0F9F /* TextFieldCell.xib */; }; @@ -46,7 +52,13 @@ CC19AB261D7DAB7F00B8B196 /* PickerControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PickerControl.swift; sourceTree = ""; }; CC28B4081D7D060B00B7CA97 /* CustomHeaderFooterView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CustomHeaderFooterView.xib; sourceTree = ""; }; CC28B40A1D7D064700B7CA97 /* CustomHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomHeader.swift; sourceTree = ""; }; + CC2B23AD1D84DF4B0025CDF8 /* ActionBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ActionBar.swift; path = ActionBar/ActionBar.swift; sourceTree = ""; }; CC5234571D730EF500F312E7 /* SelectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectionViewController.swift; sourceTree = ""; }; + CCBD2FBB1D87344E00C61DCC /* ActionBarManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ActionBarManager.swift; path = ActionBar/ActionBarManager.swift; sourceTree = ""; }; + CCBD2FBE1D874BBA00C61DCC /* Rules.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rules.swift; sourceTree = ""; }; + CCBD2FBF1D874BBA00C61DCC /* Validation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validation.swift; sourceTree = ""; }; + CCBD2FC01D874BBA00C61DCC /* ValidationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationError.swift; sourceTree = ""; }; + CCBD2FC11D874BBA00C61DCC /* Validator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validator.swift; sourceTree = ""; }; CCDDB6951D7DA9CB006C0F9F /* CustomItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomItem.swift; sourceTree = ""; }; CCDDB6961D7DA9CB006C0F9F /* TextFieldItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldItem.swift; sourceTree = ""; }; CCDDB6991D7DA9DE006C0F9F /* TextFieldCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextFieldCell.xib; sourceTree = ""; }; @@ -96,6 +108,8 @@ 257F72C31D07204A00F7B954 /* SampleApp */ = { isa = PBXGroup; children = ( + CCBD2FBD1D874BBA00C61DCC /* Validator */, + CCBD2FC61D874BFC00C61DCC /* ActionBar */, 257F72C41D07204A00F7B954 /* AppDelegate.swift */, CC5234571D730EF500F312E7 /* SelectionViewController.swift */, 257F72C61D07204A00F7B954 /* ViewController.swift */, @@ -132,6 +146,26 @@ name = Frameworks; sourceTree = ""; }; + CCBD2FBD1D874BBA00C61DCC /* Validator */ = { + isa = PBXGroup; + children = ( + CCBD2FBE1D874BBA00C61DCC /* Rules.swift */, + CCBD2FBF1D874BBA00C61DCC /* Validation.swift */, + CCBD2FC01D874BBA00C61DCC /* ValidationError.swift */, + CCBD2FC11D874BBA00C61DCC /* Validator.swift */, + ); + path = Validator; + sourceTree = ""; + }; + CCBD2FC61D874BFC00C61DCC /* ActionBar */ = { + isa = PBXGroup; + children = ( + CC2B23AD1D84DF4B0025CDF8 /* ActionBar.swift */, + CCBD2FBB1D87344E00C61DCC /* ActionBarManager.swift */, + ); + name = ActionBar; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -180,11 +214,13 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = ODIGEO; TargetAttributes = { 257F72C01D07204A00F7B954 = { CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = 7M6E9TXYGF; + LastSwiftMigration = 0800; }; 25FA9E171D24123100E2C899 = { CreatedOnToolsVersion = 7.3.1; @@ -288,11 +324,17 @@ files = ( CCDDB6981D7DA9CB006C0F9F /* TextFieldItem.swift in Sources */, 257F72C71D07204A00F7B954 /* ViewController.swift in Sources */, + CCBD2FC31D874BBA00C61DCC /* Validation.swift in Sources */, CC28B40B1D7D064700B7CA97 /* CustomHeader.swift in Sources */, + CCBD2FC51D874BBA00C61DCC /* Validator.swift in Sources */, CC5234581D730EF500F312E7 /* SelectionViewController.swift in Sources */, + CCBD2FC41D874BBA00C61DCC /* ValidationError.swift in Sources */, CC19AB271D7DAB7F00B8B196 /* PickerControl.swift in Sources */, + CCBD2FBC1D87344E00C61DCC /* ActionBarManager.swift in Sources */, + CC2B23AE1D84DF4B0025CDF8 /* ActionBar.swift in Sources */, 257F72C51D07204A00F7B954 /* AppDelegate.swift in Sources */, CCDDB6971D7DA9CB006C0F9F /* CustomItem.swift in Sources */, + CCBD2FC21D874BBA00C61DCC /* Rules.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -347,8 +389,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -392,8 +436,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -412,6 +458,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.3; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; }; name = Release; @@ -420,14 +467,16 @@ isa = XCBuildConfiguration; baseConfigurationReference = 224FEB30105CBE2EBFFC9EE9 /* Pods-SampleApp.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 7M6E9TXYGF; INFOPLIST_FILE = "$(SRCROOT)/SampleApp/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.odigeo.tableviewkit.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -435,13 +484,15 @@ isa = XCBuildConfiguration; baseConfigurationReference = D368DEDD0DD5DD15CD2C3F65 /* Pods-SampleApp.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 7M6E9TXYGF; INFOPLIST_FILE = "$(SRCROOT)/SampleApp/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.odigeo.tableviewkit.example; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/Examples/SampleApp/SampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/SampleApp/SampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..3f76a7e --- /dev/null +++ b/Examples/SampleApp/SampleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/SampleApp/SampleApp.xcodeproj/xcshareddata/xcschemes/SampleApp.xcscheme b/Examples/SampleApp/SampleApp.xcodeproj/xcshareddata/xcschemes/SampleApp.xcscheme index cef52a5..9824a21 100644 --- a/Examples/SampleApp/SampleApp.xcodeproj/xcshareddata/xcschemes/SampleApp.xcscheme +++ b/Examples/SampleApp/SampleApp.xcodeproj/xcshareddata/xcschemes/SampleApp.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + diff --git a/TableViewKit/Controls/ActionBar.swift b/Examples/SampleApp/SampleApp/ActionBar/ActionBar.swift similarity index 70% rename from TableViewKit/Controls/ActionBar.swift rename to Examples/SampleApp/SampleApp/ActionBar/ActionBar.swift index e73e7f8..fa1a162 100644 --- a/TableViewKit/Controls/ActionBar.swift +++ b/Examples/SampleApp/SampleApp/ActionBar/ActionBar.swift @@ -16,11 +16,11 @@ public enum Direction { public protocol ActionBarDelegate { - func actionBar(actionBar: ActionBar, direction: Direction) -> NSIndexPath? - func actionBar(actionBar: ActionBar, doneButtonPressed doneButtonItem: UIBarButtonItem) + func actionBar(_ actionBar: ActionBar, direction: Direction) + func actionBar(_ actionBar: ActionBar, doneButtonPressed doneButtonItem: UIBarButtonItem) } -public class ActionBar: UIToolbar { +open class ActionBar: UIToolbar { var navigationControl: UISegmentedControl! var actionBarDelegate: ActionBarDelegate! @@ -40,29 +40,29 @@ public class ActionBar: UIToolbar { super.init(coder: aDecoder)! } - private func setup() { + fileprivate func setup() { let previousButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.init(rawValue: 105)!, target: self, action: #selector(previousHandler)) let nextButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.init(rawValue: 106)!, target: self, action: #selector(nextHandler)) - let doneButton = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: #selector(handleActionBarDone)) + let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleActionBarDone)) - let spacer = UIBarButtonItem(barButtonSystemItem: .FixedSpace, target: nil, action: nil) + let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) spacer.width = 40.0 - let flexible = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil) + let flexible = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) items = [previousButtonItem, spacer, nextButtonItem, flexible, doneButton] } - @objc func handleActionBarDone(item: UIBarButtonItem) { + @objc func handleActionBarDone(_ item: UIBarButtonItem) { actionBarDelegate.actionBar(self, doneButtonPressed: item) } - @objc func previousHandler(sender: UIBarButtonItem) { + @objc func previousHandler(_ sender: UIBarButtonItem) { actionBarDelegate.actionBar(self, direction: .previous) } - @objc func nextHandler(sender: UIBarButtonItem) { + @objc func nextHandler(_ sender: UIBarButtonItem) { actionBarDelegate.actionBar(self, direction: .next) } } diff --git a/Examples/SampleApp/SampleApp/ActionBar/ActionBarManager.swift b/Examples/SampleApp/SampleApp/ActionBar/ActionBarManager.swift new file mode 100644 index 0000000..13b5470 --- /dev/null +++ b/Examples/SampleApp/SampleApp/ActionBar/ActionBarManager.swift @@ -0,0 +1,61 @@ +// +// ActionBarManager.swift +// SampleApp +// +// Created by Alfredo Delli Bovi on 12/09/2016. +// Copyright © 2016 ODIGEO. All rights reserved. +// + +import Foundation +import TableViewKit + +class ActionBarManager: ActionBarDelegate { + + let manager: TableViewManager + + init(manager: TableViewManager) { + self.manager = manager + } + + public func actionBar(_ actionBar: ActionBar, direction: Direction) { + guard let indexPath = indexPathForResponder(forDirection: direction) else { return } + + manager.tableView.scrollToRow(at: indexPath, at: .top, animated: true) + manager.tableView.cellForRow(at: indexPath)?.becomeFirstResponder() + } + + public func actionBar(_ actionBar: ActionBar, doneButtonPressed doneButtonItem: UIBarButtonItem) { } + + fileprivate func indexPathForResponder(forDirection direction: Direction) -> IndexPath? { + + func isFirstResponder(item: Item) -> Bool { + if isResponder(item: item), + let indexPath = item.indexPath(in: manager), + manager.tableView.cellForRow(at: indexPath)?.isFirstResponder == true { + return true + } + return false + } + + func isResponder(item: Item) -> Bool { + return (item as? UIResponder)?.canBecomeFirstResponder ?? false + } + + let array = manager.sections.flatMap { $0.items } + guard let currentItem = array.first(where: isFirstResponder), + let index = array.index(of: currentItem) + else { return nil } + + let item: Item? + + switch direction { + case .next: + item = array.suffix(from: index).dropFirst().first(where: isResponder) + case .previous: + item = array.prefix(upTo: index).reversed().first(where: isResponder) + } + + return item?.indexPath(in: manager) + + } +} diff --git a/Examples/SampleApp/SampleApp/AppDelegate.swift b/Examples/SampleApp/SampleApp/AppDelegate.swift index 09fd24e..91342bd 100644 --- a/Examples/SampleApp/SampleApp/AppDelegate.swift +++ b/Examples/SampleApp/SampleApp/AppDelegate.swift @@ -13,32 +13,31 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + private func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: Any]?) -> Bool { // Override point for customization after application launch. return true } - func applicationWillResignActive(application: UIApplication) { + func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - func applicationDidEnterBackground(application: UIApplication) { + func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - func applicationWillEnterForeground(application: UIApplication) { + func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - func applicationDidBecomeActive(application: UIApplication) { + func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - func applicationWillTerminate(application: UIApplication) { + func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } diff --git a/Examples/SampleApp/SampleApp/Base.lproj/Main.storyboard b/Examples/SampleApp/SampleApp/Base.lproj/Main.storyboard index 44147c7..799aaec 100644 --- a/Examples/SampleApp/SampleApp/Base.lproj/Main.storyboard +++ b/Examples/SampleApp/SampleApp/Base.lproj/Main.storyboard @@ -1,8 +1,9 @@ - - + + - + + @@ -26,18 +27,17 @@ - + - + - + - - - - + + + diff --git a/Examples/SampleApp/SampleApp/CustomHeader.swift b/Examples/SampleApp/SampleApp/CustomHeader.swift index aba31f0..3c5cb05 100644 --- a/Examples/SampleApp/SampleApp/CustomHeader.swift +++ b/Examples/SampleApp/SampleApp/CustomHeader.swift @@ -17,10 +17,10 @@ public class CustomHeaderFooterView: UITableViewHeaderFooterView { public class CustomHeaderDrawer: HeaderFooterDrawer { - public static let nib = UINib(nibName: String(CustomHeaderFooterView.self), bundle: nil) - static public var headerFooterType = HeaderFooterType.Nib(CustomHeaderDrawer.nib, CustomHeaderFooterView.self) + public static let nib = UINib(nibName: String(describing: CustomHeaderFooterView.self), bundle: nil) + static public var type = HeaderFooterType.nib(CustomHeaderDrawer.nib, CustomHeaderFooterView.self) - static public func draw(view: UITableViewHeaderFooterView, withItem item: Any) { + static public func draw(_ view: UITableViewHeaderFooterView, with item: Any) { let item = item as! CustomHeaderItem let view = view as! CustomHeaderFooterView view.label.text = item.title @@ -31,7 +31,7 @@ public class CustomHeaderDrawer: HeaderFooterDrawer { public class CustomHeaderItem: HeaderFooter { public var title: String? - public var height: ImmutableMutableHeight? = ImmutableMutableHeight.mutable(44.0) + public var height: Height? = .dynamic(44.0) public var drawer: HeaderFooterDrawer.Type = CustomHeaderDrawer.self diff --git a/Examples/SampleApp/SampleApp/CustomItem.swift b/Examples/SampleApp/SampleApp/CustomItem.swift index 9b26f6d..604931a 100644 --- a/Examples/SampleApp/SampleApp/CustomItem.swift +++ b/Examples/SampleApp/SampleApp/CustomItem.swift @@ -12,9 +12,9 @@ import TableViewKit public class CustomDrawer: CellDrawer { - static public var cellType = CellType.Class(BaseCell.self) + static public var type = CellType.class(UITableViewCell.self) - static public func draw(cell: BaseCell, withItem item: Any) { + static public func draw(_ cell: UITableViewCell, with item: Any) { let item = item as! CustomItem cell.accessoryType = item.accessoryType cell.accessoryView = item.accessoryView @@ -29,8 +29,8 @@ public class CustomItem: Selectable, Item { public var onSelection: (Selectable) -> () = { _ in } - public var cellStyle: UITableViewCellStyle = .Default - public var accessoryType: UITableViewCellAccessoryType = .None + public var cellStyle: UITableViewCellStyle = .default + public var accessoryType: UITableViewCellAccessoryType = .none public var accessoryView: UIView? public var cellHeight: CGFloat? = UITableViewAutomaticDimension diff --git a/Examples/SampleApp/SampleApp/PickerControl.swift b/Examples/SampleApp/SampleApp/PickerControl.swift index 4c1571d..4fbd175 100644 --- a/Examples/SampleApp/SampleApp/PickerControl.swift +++ b/Examples/SampleApp/SampleApp/PickerControl.swift @@ -35,24 +35,24 @@ public protocol PickerItemProtocol: class { } public enum PickerControlType { - case Single, MultiColumn, Date + case single, multiColumn, date } public enum PickerControlDismissType { - case None, Select, Cancel + case none, select, cancel } -public class PickerItem: PickerItemProtocol, CustomStringConvertible { +open class PickerItem: PickerItemProtocol, CustomStringConvertible { - public var title: String - public var value: Any + open var title: String + open var value: Any public required init(title: String, value: Any) { self.title = title self.value = value } - public var description: String { + open var description: String { return title } } @@ -60,69 +60,69 @@ public class PickerItem: PickerItemProtocol, CustomStringConvertible { public typealias SelectCallBack = (PickerControl, Any) -> () public typealias CancelCallBack = (PickerControl) -> () -public class PickerControl: NSObject { +open class PickerControl: NSObject { - private var type: PickerControlType - private var dismissType: PickerControlDismissType + fileprivate var type: PickerControlType + fileprivate var dismissType: PickerControlDismissType // Single columns - private var items: [PickerItemProtocol]! - private var selection: PickerItemProtocol! + fileprivate var items: [PickerItemProtocol]! + fileprivate var selection: PickerItemProtocol! // Multicolumn - private var components: [[PickerItemProtocol]]! - private var selections: [PickerItemProtocol!]! + fileprivate var components: [[PickerItemProtocol]]! + fileprivate var selections: [PickerItemProtocol?]! // Date - private var dateSelection: NSDate? + fileprivate var dateSelection: Date? - private var pickerView: UIPickerView? - private var datePicker: UIDatePicker? + fileprivate var pickerView: UIPickerView? + fileprivate var datePicker: UIDatePicker? - private var overlayLayerView: UIView! - private var pickerContainerView: UIView! - private var navigationBar: UINavigationBar! + fileprivate var overlayLayerView: UIView! + fileprivate var pickerContainerView: UIView! + fileprivate var navigationBar: UINavigationBar! - public var headerView: UIView? - public var cancelButtonItem: UIBarButtonItem! - public var okButtonItem: UIBarButtonItem! + open var headerView: UIView? + open var cancelButtonItem: UIBarButtonItem! + open var okButtonItem: UIBarButtonItem! - public var title: String? - public var selectCallback: SelectCallBack? - public var cancelCallback: CancelCallBack? + open var title: String? + open var selectCallback: SelectCallBack? + open var cancelCallback: CancelCallBack? // MARK: Constructors public override init() { - type = .Single - dismissType = .Cancel + type = .single + dismissType = .cancel items = [] components = [] overlayLayerView = UIView() - overlayLayerView.userInteractionEnabled = true + overlayLayerView.isUserInteractionEnabled = true overlayLayerView.backgroundColor = UIColor(white: 0, alpha: 0.7) pickerContainerView = UIView() - pickerContainerView.backgroundColor = UIColor.whiteColor() + pickerContainerView.backgroundColor = UIColor.white super.init() pickerView = UIPickerView() pickerView?.dataSource = self pickerView?.delegate = self - pickerView?.autoresizingMask = .FlexibleWidth + pickerView?.autoresizingMask = .flexibleWidth datePicker = UIDatePicker() - datePicker?.autoresizingMask = .FlexibleWidth - datePicker?.addTarget(self, action: #selector(datePickerDidChangeValue), forControlEvents: .ValueChanged) + datePicker?.autoresizingMask = .flexibleWidth + datePicker?.addTarget(self, action: #selector(datePickerDidChangeValue), for: .valueChanged) navigationBar = UINavigationBar() - cancelButtonItem = UIBarButtonItem(title: NSLocalizedString("Cancel", comment: ""), style: .Plain, target: self, action: #selector(cancelButtonPressed)) - okButtonItem = UIBarButtonItem(title: NSLocalizedString("OK", comment: ""), style: .Plain, target: self, action: #selector(selectButtonPressed)) + cancelButtonItem = UIBarButtonItem(title: NSLocalizedString("Cancel", comment: ""), style: .plain, target: self, action: #selector(cancelButtonPressed)) + okButtonItem = UIBarButtonItem(title: NSLocalizedString("OK", comment: ""), style: .plain, target: self, action: #selector(selectButtonPressed)) } public convenience init(elements: [AnyObject], selectCallback: SelectCallBack? = nil, cancelCallback: CancelCallBack? = nil) { @@ -138,7 +138,7 @@ public class PickerControl: NSObject { // We have a array else if let array = value as? [AnyObject] { - type = .MultiColumn + type = .multiColumn // Column items var components: [PickerItemProtocol] = [] @@ -149,18 +149,18 @@ public class PickerControl: NSObject { components.append(element as! PickerItemProtocol) } else { - let item = PickerItem(title: String(element), value: element) + let item = PickerItem(title: String(describing: element), value: element) components.append(item) } } if components.count != 0 { self.components.append(components) - self.selections = Array.init(count: self.components.count, repeatedValue: nil) + self.selections = Array.init(repeating: nil, count: self.components.count) } } else { - let item = PickerItem(title: String(value), value: value) + let item = PickerItem(title: String(describing: value), value: value) items.append(item) } } @@ -169,11 +169,11 @@ public class PickerControl: NSObject { self.cancelCallback = cancelCallback } - public convenience init(datePickerMode: UIDatePickerMode, fromDate: NSDate, toDate: NSDate, minuteInterval: Int, selectCallback: SelectCallBack? = nil, cancelCallback: CancelCallBack? = nil) { + public convenience init(datePickerMode: UIDatePickerMode, fromDate: Date, toDate: Date, minuteInterval: Int, selectCallback: SelectCallBack? = nil, cancelCallback: CancelCallBack? = nil) { self.init() - type = .Date + type = .date datePicker?.minimumDate = fromDate datePicker?.maximumDate = toDate @@ -186,7 +186,7 @@ public class PickerControl: NSObject { // MARK: Public methods - public func presentPickerOnView(view: UIView) { + open func presentPickerOnView(_ view: UIView) { let hostFrame = view.window!.frame @@ -198,64 +198,64 @@ public class PickerControl: NSObject { view.window?.addSubview(overlayLayerView) // Add gesture - if dismissType == .Select { + if dismissType == .select { let layerTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(selectButtonPressed)) overlayLayerView.addGestureRecognizer(layerTapRecognizer) } - else if dismissType == .Cancel { + else if dismissType == .cancel { let layerTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(cancelButtonPressed)) overlayLayerView.addGestureRecognizer(layerTapRecognizer) } let extraHeight = headerView != nil ? CGFloat(Constants.Frame.HeaderViewHeight) : 0 - let pickerContainerSourceFrame = CGRectMake(0, CGRectGetHeight(hostFrame), CGRectGetWidth(hostFrame), CGFloat(Constants.Frame.PickerHeight) + extraHeight) + let pickerContainerSourceFrame = CGRect(x: 0, y: hostFrame.height, width: hostFrame.width, height: CGFloat(Constants.Frame.PickerHeight) + extraHeight) pickerContainerView.frame = pickerContainerSourceFrame view.window?.addSubview(pickerContainerView) // Add toolbar - navigationBar.frame = CGRectMake(0, 0, CGRectGetWidth(hostFrame), CGFloat(Constants.Frame.NavigationBarHeight)) + navigationBar.frame = CGRect(x: 0, y: 0, width: hostFrame.width, height: CGFloat(Constants.Frame.NavigationBarHeight)) pickerContainerView.addSubview(navigationBar) - let spacer = UIBarButtonItem(barButtonSystemItem: .FixedSpace, target: nil, action: nil) + let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) spacer.width = 15 let navigationItem = UINavigationItem() navigationItem.title = title navigationItem.leftBarButtonItems = [spacer, cancelButtonItem] navigationItem.rightBarButtonItems = [spacer, okButtonItem] - navigationBar.pushNavigationItem(navigationItem, animated: false) + navigationBar.pushItem(navigationItem, animated: false) if let headerView = headerView { - headerView.frame = CGRectMake(0, CGRectGetMaxY(navigationBar.frame), CGRectGetWidth(hostFrame), CGFloat(Constants.Frame.NavigationBarHeight)) + headerView.frame = CGRect(x: 0, y: navigationBar.frame.maxY, width: hostFrame.width, height: CGFloat(Constants.Frame.NavigationBarHeight)) pickerContainerView.addSubview(headerView) } - let pickerViewFrame = CGRectMake(0, CGFloat(Constants.Frame.NavigationBarHeight) + extraHeight, CGRectGetWidth(hostFrame), CGFloat(Constants.Frame.PickerViewHeight)) - if type == .Single || type == .MultiColumn { + let pickerViewFrame = CGRect(x: 0, y: CGFloat(Constants.Frame.NavigationBarHeight) + extraHeight, width: hostFrame.width, height: CGFloat(Constants.Frame.PickerViewHeight)) + if type == .single || type == .multiColumn { pickerView?.frame = pickerViewFrame pickerContainerView.addSubview(pickerView!) } - else if type == .Date { + else if type == .date { datePicker?.frame = pickerViewFrame pickerContainerView.addSubview(datePicker!) } - UIView.animateWithDuration(Constants.Animation.Duration, animations: { + UIView.animate(withDuration: Constants.Animation.Duration, animations: { self.overlayLayerView.alpha = 1.0 }) - let pickerContainerDestinationFrame = CGRectMake(0, CGRectGetHeight(hostFrame) - CGFloat(Constants.Frame.PickerHeight) - extraHeight, CGRectGetWidth(hostFrame), CGFloat(Constants.Frame.PickerHeight) + extraHeight) - UIView.animateWithDuration(Constants.Animation.Duration, delay: 0, options: [.CurveEaseOut], animations: { + let pickerContainerDestinationFrame = CGRect(x: 0, y: hostFrame.height - CGFloat(Constants.Frame.PickerHeight) - extraHeight, width: hostFrame.width, height: CGFloat(Constants.Frame.PickerHeight) + extraHeight) + UIView.animate(withDuration: Constants.Animation.Duration, delay: 0, options: [.curveEaseOut], animations: { self.pickerContainerView.frame = pickerContainerDestinationFrame }, completion: nil) } - public func dismissPickerView() { + open func dismissPickerView() { - let pickerContainerDestinationFrame = CGRectMake(0, pickerContainerView.frame.origin.y + CGFloat(Constants.Frame.PickerHeight), pickerContainerView.frame.size.width, CGFloat(Constants.Frame.PickerHeight)) + let pickerContainerDestinationFrame = CGRect(x: 0, y: pickerContainerView.frame.origin.y + CGFloat(Constants.Frame.PickerHeight), width: pickerContainerView.frame.size.width, height: CGFloat(Constants.Frame.PickerHeight)) - UIView.animateWithDuration(Constants.Animation.Duration, animations: { + UIView.animate(withDuration: Constants.Animation.Duration, animations: { self.overlayLayerView.alpha = 0 }, completion: { finished in self.overlayLayerView.removeFromSuperview() @@ -264,16 +264,16 @@ public class PickerControl: NSObject { } }) - UIView.animateWithDuration(Constants.Animation.Duration, delay: 0, options: [.CurveEaseIn], animations: { + UIView.animate(withDuration: Constants.Animation.Duration, delay: 0, options: [.curveEaseIn], animations: { self.pickerContainerView.frame = pickerContainerDestinationFrame }, completion: { finished in self.pickerContainerView.removeFromSuperview() }) } - public func selectValue(value: PickerItemProtocol) { + open func selectValue(_ value: PickerItemProtocol) { - if type != .Single { + if type != .single { return } @@ -286,9 +286,9 @@ public class PickerControl: NSObject { } } - public func selectValue(value: PickerItemProtocol, component: Int) { + open func selectValue(_ value: PickerItemProtocol, component: Int) { - if type != .MultiColumn { + if type != .multiColumn { return } @@ -302,27 +302,27 @@ public class PickerControl: NSObject { } } - public func selectValue(index: Int) { + open func selectValue(_ index: Int) { - if type != .Single { + if type != .single { return } pickerView?.selectRow(index, inComponent: 0, animated: false) } - public func selectValue(index: Int, component: Int) { + open func selectValue(_ index: Int, component: Int) { - if type != .MultiColumn { + if type != .multiColumn { return } pickerView?.selectRow(index, inComponent: component, animated: false) } - public func selectDate(date: NSDate) { + open func selectDate(_ date: Date) { - if type != .Date { + if type != .date { return } @@ -331,31 +331,31 @@ public class PickerControl: NSObject { // MARK: Private methods - private func updateSelection() { + fileprivate func updateSelection() { - if type == .Single { + if type == .single { if items.count == 0 { return } - let index = pickerView!.selectedRowInComponent(0) + let index = pickerView!.selectedRow(inComponent: 0) let item = items[index] selection = item } - else if type == .MultiColumn { + else if type == .multiColumn { if components.count == 0 { return } for columnIndex in 0 ..< pickerView!.numberOfComponents { - let rowIndex = pickerView!.selectedRowInComponent(columnIndex) + let rowIndex = pickerView!.selectedRow(inComponent: columnIndex) let item = components[columnIndex][rowIndex] selections[columnIndex] = item } } - else if type == .Date { + else if type == .date { dateSelection = datePicker?.date } @@ -377,15 +377,15 @@ public class PickerControl: NSObject { updateSelection() - if type == .Single { + if type == .single { selectCallback?(self, selection) } - else if type == .MultiColumn { + else if type == .multiColumn { selectCallback?(self, selections) } - else if type == .Date { + else if type == .date { selectCallback?(self, dateSelection) } @@ -396,24 +396,24 @@ public class PickerControl: NSObject { extension PickerControl: UIPickerViewDataSource { - @objc public func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int { + @objc public func numberOfComponents(in pickerView: UIPickerView) -> Int { - if type == .Single { + if type == .single { return 1 } - else if type == .MultiColumn { + else if type == .multiColumn { return components.count } return 0 } - @objc public func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + @objc public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { - if type == .Single { + if type == .single { return items.count } - else if type == .MultiColumn { + else if type == .multiColumn { let column = components[component] return column.count } @@ -421,14 +421,14 @@ extension PickerControl: UIPickerViewDataSource { return 0 } - @objc public func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + @objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { - if type == .Single { + if type == .single { let item = items[row] return item.title } - else if type == .MultiColumn { + else if type == .multiColumn { let column = components[component] let item = column[row] @@ -441,7 +441,7 @@ extension PickerControl: UIPickerViewDataSource { extension PickerControl: UIPickerViewDelegate { - public func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { updateSelection() } @@ -449,9 +449,9 @@ extension PickerControl: UIPickerViewDelegate { extension PickerControl: UIToolbarDelegate { - public func positionForBar(bar: UIBarPositioning) -> UIBarPosition { + public func position(for bar: UIBarPositioning) -> UIBarPosition { - return .Top + return .top } } diff --git a/Examples/SampleApp/SampleApp/SelectionViewController.swift b/Examples/SampleApp/SampleApp/SelectionViewController.swift index ce31e9d..7d44257 100644 --- a/Examples/SampleApp/SampleApp/SelectionViewController.swift +++ b/Examples/SampleApp/SampleApp/SelectionViewController.swift @@ -37,7 +37,7 @@ public class SelectionItem: SelectionItemProtocol { public var onSelection: (Selectable) -> () = { _ in } - public var accessoryType: UITableViewCellAccessoryType = .None + public var accessoryType: UITableViewCellAccessoryType = .none public var accessoryView: UIView? public var cellHeight: CGFloat? = UITableViewAutomaticDimension @@ -67,7 +67,7 @@ public class SelectionViewController: UITableViewController { selectedItems = [] } - override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) commonInit() } @@ -98,7 +98,7 @@ public class SelectionViewController: UITableViewController { setupTaleViewItems() } - public override func viewWillDisappear(animated: Bool) { + public override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) @@ -113,14 +113,14 @@ public class SelectionViewController: UITableViewController { private func setupTaleViewItems() { let section = SelectionSection() - tableViewManager.sections.append(section) + tableViewManager.sections.insert(section, at: 0) for element in items { element.onSelection = { item in - self.toogleItemCheck(item as! SelectionItem) + self.toogleItemCheck(item: item as! SelectionItem) } - element.accessoryType = element.selected ? .Checkmark : .None + element.accessoryType = element.selected ? .checkmark : .none section.items.append(element) } @@ -133,14 +133,14 @@ public class SelectionViewController: UITableViewController { if let checkedItem = itemSelected() { checkedItem.selected = false - checkedItem.accessoryType = .None - checkedItem.reloadRow(inManager: tableViewManager, withAnimation: .Fade) + checkedItem.accessoryType = .none + checkedItem.reload(in: tableViewManager, with: .fade) } } item.selected = !item.selected - item.accessoryType = item.accessoryType == .Checkmark ? .None : .Checkmark - item.reloadRow(inManager: tableViewManager, withAnimation: .Fade) + item.accessoryType = item.accessoryType == .checkmark ? .none : .checkmark + item.reload(in: tableViewManager, with: .fade) fillSelected() } diff --git a/Examples/SampleApp/SampleApp/TextFieldItem.swift b/Examples/SampleApp/SampleApp/TextFieldItem.swift index d7701eb..29d3d3f 100644 --- a/Examples/SampleApp/SampleApp/TextFieldItem.swift +++ b/Examples/SampleApp/SampleApp/TextFieldItem.swift @@ -9,7 +9,9 @@ import Foundation import TableViewKit -public class TextFieldCell: BaseCell { +public class TextFieldCell: UITableViewCell, ItemCompatible, ActionBarDelegate { + + public var item: Item? public var textFieldItem: TextFieldItem { get { @@ -23,25 +25,42 @@ public class TextFieldCell: BaseCell { super.awakeFromNib() - selectionStyle = .None - responder = textField + selectionStyle = .none - textField.addTarget(self, action: #selector(onTextChange), forControlEvents: .EditingChanged) - textField.inputAccessoryView = actionBar + textField.addTarget(self, action: #selector(onTextChange), for: .editingChanged) + textField.inputAccessoryView = ActionBar(delegate: self) + } public func onTextChange(textField: UITextField) { textFieldItem.value = textField.text } + + public func actionBar(_ actionBar: ActionBar, direction: Direction) { + textFieldItem.actionBarDelegate.actionBar(actionBar, direction: direction) + } + public func actionBar(_ actionBar: ActionBar, doneButtonPressed doneButtonItem: UIBarButtonItem) { + textField.resignFirstResponder() + } + + override open var isFirstResponder: Bool { + get { + return textField.isFirstResponder + } + } + + override open func becomeFirstResponder() -> Bool { + return textField.becomeFirstResponder() + } } public class TextFieldDrawer: CellDrawer { - public static let nib = UINib(nibName: String(TextFieldCell.self), bundle: nil) - public static let cellType = CellType.Nib(TextFieldDrawer.nib, TextFieldCell.self) + public static let nib = UINib(nibName: String(describing: TextFieldCell.self), bundle: nil) + public static let type = CellType.nib(TextFieldDrawer.nib, TextFieldCell.self) - public static func draw(cell: BaseCell, withItem item: Any) { + public static func draw(_ cell: UITableViewCell, with item: Any) { let textCell = cell as! TextFieldCell let textItem = item as! TextFieldItem @@ -51,7 +70,7 @@ public class TextFieldDrawer: CellDrawer { } } -public class TextFieldItem: Item, ContentValidatable, Validationable { +public class TextFieldItem: UIResponder, Item, ContentValidatable, Validationable { public var drawer: CellDrawer.Type = TextFieldDrawer.self @@ -62,8 +81,11 @@ public class TextFieldItem: Item, ContentValidatable, Validationable { public var placeHolder: String? public var value: String? - public init(placeHolder: String?) { + fileprivate let actionBarDelegate: ActionBarDelegate + + public init(placeHolder: String?, actionBarDelegate: ActionBarDelegate) { self.placeHolder = placeHolder + self.actionBarDelegate = actionBarDelegate } public var validationContent: String? { @@ -71,4 +93,10 @@ public class TextFieldItem: Item, ContentValidatable, Validationable { return value } } + + override open var canBecomeFirstResponder: Bool { + get { + return true + } + } } diff --git a/TableViewKit/Validator/Rules.swift b/Examples/SampleApp/SampleApp/Validator/Rules.swift similarity index 91% rename from TableViewKit/Validator/Rules.swift rename to Examples/SampleApp/SampleApp/Validator/Rules.swift index c3ce400..df4f694 100644 --- a/TableViewKit/Validator/Rules.swift +++ b/Examples/SampleApp/SampleApp/Validator/Rules.swift @@ -15,7 +15,7 @@ public struct ExistRule: Validatable { error = NSError(domain: "Validator", code: 101, userInfo: [NSLocalizedDescriptionKey: "This value can´t be empty"]) } - public func test(validationContent: String?) -> Bool { + public func test(_ validationContent: String?) -> Bool { guard validationContent != nil else { return false } return true } @@ -36,7 +36,7 @@ public struct CharactersLengthRule: Validatable { self.error = NSError(domain: "Validator", code: 101, userInfo: [NSLocalizedDescriptionKey: "It should have between \(min) and \(max) characters"]) } - public func test(validationContent: String?) -> Bool { + public func test(_ validationContent: String?) -> Bool { guard let validationContent = validationContent else { return true } return (min...max ~= validationContent.characters.count) } @@ -56,7 +56,7 @@ public struct NumberBetweenRule: Validatable { self.error = NSError(domain: "Validator", code: 101, userInfo: [NSLocalizedDescriptionKey: "It should be between \(min) and \(max)"]) } - public func test(validationContent: Int) -> Bool { + public func test(_ validationContent: Int) -> Bool { return min...max ~= validationContent } } diff --git a/TableViewKit/Validator/Validation.swift b/Examples/SampleApp/SampleApp/Validator/Validation.swift similarity index 69% rename from TableViewKit/Validator/Validation.swift rename to Examples/SampleApp/SampleApp/Validator/Validation.swift index e515881..0bb457a 100644 --- a/TableViewKit/Validator/Validation.swift +++ b/Examples/SampleApp/SampleApp/Validator/Validation.swift @@ -13,19 +13,19 @@ struct AnyValidatable: Validatable { let rule: Any let error: NSError? - init(base: R) { + init(base: R) where R.Input == Input { self.testContainer = base.test self.rule = base self.error = base.error } - func test(validationContent: Input) -> Bool { + func test(_ validationContent: Input) -> Bool { return self.testContainer(validationContent) } } -public class Validation { +open class Validation { let forInput: () -> Input let identifier: Any? var rules: [AnyValidatable] = [] @@ -39,23 +39,23 @@ public class Validation { } } - public init(forInput: () -> Input, withIdentifier identifier: Any? = nil) { + public init(forInput: @escaping () -> Input, withIdentifier identifier: Any? = nil) { self.forInput = forInput self.identifier = identifier } - public init(forInput input: C, withIdentifier identifier: Any? = nil) { + public init(forInput input: C, withIdentifier identifier: Any? = nil) where C.Input == Input { self.forInput = { input.validationContent } self.identifier = identifier } - public init(forInput: () -> Input, withIdentifier identifier: Any? = nil, rule: R) { + public init(forInput: @escaping () -> Input, withIdentifier identifier: Any? = nil, rule: R) where R.Input == Input { self.forInput = forInput self.identifier = identifier self.rules.append(AnyValidatable.init(base: rule)) } - public func add(rule rule: R) { + open func add(rule: R) where R.Input == Input { self.rules.append(AnyValidatable.init(base: rule)) } } diff --git a/TableViewKit/Validator/ValidationError.swift b/Examples/SampleApp/SampleApp/Validator/ValidationError.swift similarity index 100% rename from TableViewKit/Validator/ValidationError.swift rename to Examples/SampleApp/SampleApp/Validator/ValidationError.swift diff --git a/TableViewKit/Validator/Validator.swift b/Examples/SampleApp/SampleApp/Validator/Validator.swift similarity index 68% rename from TableViewKit/Validator/Validator.swift rename to Examples/SampleApp/SampleApp/Validator/Validator.swift index 456cd7b..99a4d1d 100644 --- a/TableViewKit/Validator/Validator.swift +++ b/Examples/SampleApp/SampleApp/Validator/Validator.swift @@ -21,7 +21,7 @@ public protocol Validatable { associatedtype Input var error: NSError? { get } - func test(validationContent: Input) -> Bool + func test(_ validationContent: Input) -> Bool } @@ -30,25 +30,25 @@ public protocol Regexable { } public extension Regexable where Self: Validatable { - func test(validationContent: String?) -> Bool { - return NSPredicate(format: "SELF MATCHES %@", regex).evaluateWithObject(validationContent) + func test(_ validationContent: String?) -> Bool { + return NSPredicate(format: "SELF MATCHES %@", regex).evaluate(with: validationContent) } } public struct ValidatorManager { public var errors: [ValidationError] { get { - return validations.reduce([], combine: { $0 + $1.errors }) + return validations.reduce([], { $0 + $1.errors }) } } - private var validations: [Validation] = [] + internal var validations: [Validation] = [] - public mutating func add(getInput: () -> Input, withRule rule: R) { + public mutating func add(_ getInput: @escaping () -> Input, withRule rule: R) where R.Input == Input { validations.append(Validation.init(forInput: getInput, rule: rule)) } - public mutating func add(validation validation: Validation) { + public mutating func add(validation: Validation) { guard (!validations.contains { $0 === validation }) else { return } validations.append(validation) } diff --git a/TableViewKitTests/ValidatorTests.swift b/Examples/SampleApp/SampleApp/ValidatorTests.swift similarity index 100% rename from TableViewKitTests/ValidatorTests.swift rename to Examples/SampleApp/SampleApp/ValidatorTests.swift diff --git a/Examples/SampleApp/SampleApp/ViewController.swift b/Examples/SampleApp/SampleApp/ViewController.swift index 8410301..b320171 100644 --- a/Examples/SampleApp/SampleApp/ViewController.swift +++ b/Examples/SampleApp/SampleApp/ViewController.swift @@ -24,12 +24,12 @@ class FirstSection: Section { items.replace(with: states[currentState.rawValue]) } } - + let vc: ViewController internal var header: HeaderFooterView = .view(CustomHeaderItem(title: "First Section")) internal var footer: HeaderFooterView = .view(CustomHeaderItem(title: "Section Footer\nHola")) - + required init(vc: ViewController) { self.vc = vc @@ -38,72 +38,89 @@ class FirstSection: Section { let item3 = CustomItem(title: "Testing 2") let dateItem = CustomItem(title: "Birthday") let selectionItem = CustomItem(title: "Selection") - let textFieldItem = TextFieldItem(placeHolder: "Name") + let textFieldItem = TextFieldItem(placeHolder: "Name", actionBarDelegate: vc.actionBarManager) textFieldItem.validation.add(rule: ExistRule()) - let textFieldItem2 = TextFieldItem(placeHolder: "Surname") + let textFieldItem2 = TextFieldItem(placeHolder: "Surname", actionBarDelegate: vc.actionBarManager) textFieldItem2.validation.add(rule: ExistRule()) item.onSelection = { item in - item.deselectRow(inManager: self.vc.tableViewManager, animated: true) + item.deselect(in: self.vc.tableViewManager, animated: true) self.vc.showPickerControl() } - dateItem.accessoryType = .DisclosureIndicator + dateItem.accessoryType = .disclosureIndicator dateItem.onSelection = { item in - item.deselectRow(inManager: self.vc.tableViewManager, animated: true) + item.deselect(in: self.vc.tableViewManager, animated: true) self.vc.showDatePickerControl() } - selectionItem.accessoryType = .DisclosureIndicator + selectionItem.accessoryType = .disclosureIndicator selectionItem.onSelection = { item in - item.deselectRow(inManager: self.vc.tableViewManager, animated: true) + item.deselect(in: self.vc.tableViewManager, animated: true) self.vc.showPickerControl() } - states.insert([item, dateItem, selectionItem, textFieldItem, textFieldItem2], atIndex: State.preParty.rawValue) - states.insert([item2, selectionItem, dateItem, item3, textFieldItem2, textFieldItem], atIndex: State.onParty.rawValue) - states.insert([item2], atIndex: State.afterParty.rawValue) + states.insert([item, dateItem, selectionItem, textFieldItem, textFieldItem2], at: State.preParty.rawValue) + states.insert([item2, selectionItem, dateItem, item3, textFieldItem2, textFieldItem], at: State.onParty.rawValue) + states.insert([item2], at: State.afterParty.rawValue) swap(to: .preParty) } func swap(to newState: State) { currentState = newState } - + } class SecondSection: Section { var items: ObservableArray = [] - + internal var header: HeaderFooterView = .view(CustomHeaderItem(title: "Second Section")) let vc: ViewController - + required init(vc: ViewController) { self.vc = vc let total: [Int] = Array(1...100) let items = total.map({ (index) -> Item in if (index % 2 == 0) { - let item = TextFieldItem(placeHolder: "Textfield \(index)") + let item = TextFieldItem(placeHolder: "Textfield \(index)", actionBarDelegate: vc.actionBarManager) return item } else { let item = CustomItem(title: "Label \(index)") item.onSelection = { item in - item.deselectRow(inManager: self.vc.tableViewManager, animated: true) + item.deselect(in: self.vc.tableViewManager, animated: true) } return item } }) - self.items.insertContentsOf(items, at: 0) + self.items.insert(contentsOf: items, at: 0) } } -class ViewController: UITableViewController { +protocol TableViewManagerCompatible { + var tableViewManager: TableViewManager! { get } +} + +class ViewController: UIViewController, TableViewManagerCompatible { + @IBOutlet weak var tableView: UITableView! { + didSet { + tableViewManager = TableViewManager(tableView: tableView) + } + } - var tableViewManager: TableViewManager! - var pickerControl: PickerControl? + var tableViewManager: TableViewManager! { + didSet { + actionBarManager = ActionBarManager(manager: tableViewManager) + } + } + var actionBarManager: ActionBarManager! + var validator: ValidatorManager = ValidatorManager() + - var firstSection: FirstSection! + var pickerControl: PickerControl? + var firstSection: FirstSection! + override func viewDidLoad() { super.viewDidLoad() @@ -112,16 +129,24 @@ class ViewController: UITableViewController { self.tableView.sectionFooterHeight = UITableViewAutomaticDimension; self.tableView.estimatedSectionHeaderHeight = 100; self.tableView.estimatedSectionFooterHeight = 100; - - + + firstSection = FirstSection(vc: self) - tableViewManager = TableViewManager(tableView: self.tableView, sections: [firstSection, SecondSection(vc: self)]) + tableViewManager.sections.append(firstSection) + tableViewManager.sections.append(SecondSection(vc: self)) - navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Validate", style: .Plain, target: self, action: #selector(validationAction)) - } + // TODO think about a better way to handle self registration + // In this way we do not take in consideration when the items changes + tableViewManager.sections + .flatMap { $0.items } + .flatMap { $0 as? Validationable } + .forEach { validator.add(validation: $0.validation) } + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Validate", style: .plain, target: self, action: #selector(validationAction)) + } - private func showPickerControl() { + + fileprivate func showPickerControl() { var elements = [PickerItem]() @@ -138,28 +163,31 @@ class ViewController: UITableViewController { self.pickerControl = pickerControl } - private func showDatePickerControl() { + fileprivate func showDatePickerControl() { - let toDate = NSCalendar.currentCalendar().dateByAddingUnit(.Year, value: 1, toDate: NSDate(), options: NSCalendarOptions.MatchNextTime)! - let pickerDateControl = PickerControl(datePickerMode: .Date, fromDate: NSDate(), toDate: toDate, minuteInterval: 0, selectCallback: { pickerControl, date in + let toDate = Calendar.current.date(byAdding: .year, value: 1, to: Date())! + let pickerDateControl = PickerControl(datePickerMode: .date, fromDate: Date(), toDate: toDate, minuteInterval: 0, selectCallback: { pickerControl, date in pickerControl.dismissPickerView() print(date) self.pickerControl = nil - }, cancelCallback: nil) + }, cancelCallback: nil) pickerDateControl.presentPickerOnView(view) pickerControl = pickerDateControl } - @objc private func validationAction() { + @objc fileprivate func validationAction() { firstSection.swap(to: firstSection.currentState == .preParty ? .onParty : .afterParty) - guard let error = tableViewManager.errors.first else { return } + guard let error = validator.errors.first else { + print("No errors") + return + } print(error) - } + } diff --git a/Examples/Viper/Podfile b/Examples/Viper/Podfile new file mode 100644 index 0000000..e163f9c --- /dev/null +++ b/Examples/Viper/Podfile @@ -0,0 +1,6 @@ +platform :ios, "8.0" +use_frameworks! + +target 'TableViewKit+VIPER' do + pod 'TableViewKit', :path => '../..' +end \ No newline at end of file diff --git a/Examples/Viper/TableViewKit+VIPER.xcodeproj/project.pbxproj b/Examples/Viper/TableViewKit+VIPER.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c295059 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER.xcodeproj/project.pbxproj @@ -0,0 +1,441 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 25483F9B1D7EDDE3002CF25E /* HelpCenterItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25483F9A1D7EDDE3002CF25E /* HelpCenterItem.swift */; }; + 25483F9D1D7EDE45002CF25E /* HelpCenterCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25483F9C1D7EDE45002CF25E /* HelpCenterCell.swift */; }; + 25483F9F1D7EE0EF002CF25E /* MoreAboutItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25483F9E1D7EE0EF002CF25E /* MoreAboutItem.swift */; }; + 25483FA11D7EFCFA002CF25E /* HelpCenterCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 25483FA01D7EFCFA002CF25E /* HelpCenterCell.xib */; }; + 2579BBA01D805C9E003B9CDD /* HelpCenterSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2579BB9F1D805C9E003B9CDD /* HelpCenterSection.swift */; }; + 2579BBA21D805CC2003B9CDD /* MoreAboutSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2579BBA11D805CC2003B9CDD /* MoreAboutSection.swift */; }; + 25837C7A1D87FA39001EF4B8 /* MoreAboutCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25837C791D87FA39001EF4B8 /* MoreAboutCell.swift */; }; + 25A741981D7ED45900473E02 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25A741971D7ED45900473E02 /* AppDelegate.swift */; }; + 25A7419F1D7ED45900473E02 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 25A7419E1D7ED45900473E02 /* Assets.xcassets */; }; + 25A741A21D7ED45900473E02 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 25A741A01D7ED45900473E02 /* LaunchScreen.storyboard */; }; + 25A741AB1D7ED66600473E02 /* AboutModuleProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25A741AA1D7ED66600473E02 /* AboutModuleProtocols.swift */; }; + 25A741AD1D7ED6D700473E02 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25A741AC1D7ED6D700473E02 /* AboutViewController.swift */; }; + 25D201691D7EDA410058C8A9 /* AboutPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25D201681D7EDA410058C8A9 /* AboutPresenter.swift */; }; + 25D2016B1D7EDA680058C8A9 /* AboutWireFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25D2016A1D7EDA680058C8A9 /* AboutWireFrame.swift */; }; + 8C965A5571991595F7C12D07 /* Pods_TableViewKit_VIPER.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37CDD2DD8210F1BFF7549548 /* Pods_TableViewKit_VIPER.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 25483F9A1D7EDDE3002CF25E /* HelpCenterItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HelpCenterItem.swift; path = AboutModule/HelpCenterItem.swift; sourceTree = ""; }; + 25483F9C1D7EDE45002CF25E /* HelpCenterCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HelpCenterCell.swift; path = AboutModule/HelpCenterCell.swift; sourceTree = ""; }; + 25483F9E1D7EE0EF002CF25E /* MoreAboutItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MoreAboutItem.swift; path = AboutModule/MoreAboutItem.swift; sourceTree = ""; }; + 25483FA01D7EFCFA002CF25E /* HelpCenterCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = HelpCenterCell.xib; path = AboutModule/HelpCenterCell.xib; sourceTree = ""; }; + 2579BB9F1D805C9E003B9CDD /* HelpCenterSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HelpCenterSection.swift; path = AboutModule/HelpCenterSection.swift; sourceTree = ""; }; + 2579BBA11D805CC2003B9CDD /* MoreAboutSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MoreAboutSection.swift; path = AboutModule/MoreAboutSection.swift; sourceTree = ""; }; + 25837C791D87FA39001EF4B8 /* MoreAboutCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MoreAboutCell.swift; path = AboutModule/MoreAboutCell.swift; sourceTree = ""; }; + 25A741941D7ED45900473E02 /* TableViewKit+VIPER.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TableViewKit+VIPER.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 25A741971D7ED45900473E02 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 25A7419E1D7ED45900473E02 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 25A741A11D7ED45900473E02 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 25A741A31D7ED45900473E02 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 25A741AA1D7ED66600473E02 /* AboutModuleProtocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AboutModuleProtocols.swift; path = AboutModule/AboutModuleProtocols.swift; sourceTree = ""; }; + 25A741AC1D7ED6D700473E02 /* AboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AboutViewController.swift; path = AboutModule/AboutViewController.swift; sourceTree = ""; }; + 25D201681D7EDA410058C8A9 /* AboutPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AboutPresenter.swift; path = AboutModule/AboutPresenter.swift; sourceTree = ""; }; + 25D2016A1D7EDA680058C8A9 /* AboutWireFrame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AboutWireFrame.swift; path = AboutModule/AboutWireFrame.swift; sourceTree = ""; }; + 37CDD2DD8210F1BFF7549548 /* Pods_TableViewKit_VIPER.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TableViewKit_VIPER.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9B851CD5029E3043F8A202FE /* Pods-TableViewKit+VIPER.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TableViewKit+VIPER.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TableViewKit+VIPER/Pods-TableViewKit+VIPER.debug.xcconfig"; sourceTree = ""; }; + D42E2FAD5D3651C7A308A6E1 /* Pods-TableViewKit+VIPER.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TableViewKit+VIPER.release.xcconfig"; path = "Pods/Target Support Files/Pods-TableViewKit+VIPER/Pods-TableViewKit+VIPER.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 25A741911D7ED45900473E02 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8C965A5571991595F7C12D07 /* Pods_TableViewKit_VIPER.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 25483F981D7EDDBF002CF25E /* Cells */ = { + isa = PBXGroup; + children = ( + 25837C791D87FA39001EF4B8 /* MoreAboutCell.swift */, + 25483F9C1D7EDE45002CF25E /* HelpCenterCell.swift */, + 25483FA01D7EFCFA002CF25E /* HelpCenterCell.xib */, + ); + name = Cells; + sourceTree = ""; + }; + 25483F991D7EDDC8002CF25E /* Items */ = { + isa = PBXGroup; + children = ( + 25483F9A1D7EDDE3002CF25E /* HelpCenterItem.swift */, + 25483F9E1D7EE0EF002CF25E /* MoreAboutItem.swift */, + ); + name = Items; + sourceTree = ""; + }; + 2579BB9E1D805C87003B9CDD /* Sections */ = { + isa = PBXGroup; + children = ( + 2579BB9F1D805C9E003B9CDD /* HelpCenterSection.swift */, + 2579BBA11D805CC2003B9CDD /* MoreAboutSection.swift */, + ); + name = Sections; + sourceTree = ""; + }; + 25A7418B1D7ED45900473E02 = { + isa = PBXGroup; + children = ( + 25A741961D7ED45900473E02 /* TableViewKit+VIPER */, + 25A741951D7ED45900473E02 /* Products */, + 6C4463546240C357F93598CD /* Pods */, + 7DDFC18F5AF8E912D649F658 /* Frameworks */, + ); + sourceTree = ""; + }; + 25A741951D7ED45900473E02 /* Products */ = { + isa = PBXGroup; + children = ( + 25A741941D7ED45900473E02 /* TableViewKit+VIPER.app */, + ); + name = Products; + sourceTree = ""; + }; + 25A741961D7ED45900473E02 /* TableViewKit+VIPER */ = { + isa = PBXGroup; + children = ( + 25A741A91D7ED46200473E02 /* AboutModule */, + 25A741971D7ED45900473E02 /* AppDelegate.swift */, + 25A7419E1D7ED45900473E02 /* Assets.xcassets */, + 25A741A01D7ED45900473E02 /* LaunchScreen.storyboard */, + 25A741A31D7ED45900473E02 /* Info.plist */, + ); + path = "TableViewKit+VIPER"; + sourceTree = ""; + }; + 25A741A91D7ED46200473E02 /* AboutModule */ = { + isa = PBXGroup; + children = ( + 2579BB9E1D805C87003B9CDD /* Sections */, + 25483F991D7EDDC8002CF25E /* Items */, + 25483F981D7EDDBF002CF25E /* Cells */, + 25A741AA1D7ED66600473E02 /* AboutModuleProtocols.swift */, + 25D2016A1D7EDA680058C8A9 /* AboutWireFrame.swift */, + 25D201681D7EDA410058C8A9 /* AboutPresenter.swift */, + 25A741AC1D7ED6D700473E02 /* AboutViewController.swift */, + ); + name = AboutModule; + sourceTree = ""; + }; + 6C4463546240C357F93598CD /* Pods */ = { + isa = PBXGroup; + children = ( + 9B851CD5029E3043F8A202FE /* Pods-TableViewKit+VIPER.debug.xcconfig */, + D42E2FAD5D3651C7A308A6E1 /* Pods-TableViewKit+VIPER.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 7DDFC18F5AF8E912D649F658 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 37CDD2DD8210F1BFF7549548 /* Pods_TableViewKit_VIPER.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 25A741931D7ED45900473E02 /* TableViewKit+VIPER */ = { + isa = PBXNativeTarget; + buildConfigurationList = 25A741A61D7ED45900473E02 /* Build configuration list for PBXNativeTarget "TableViewKit+VIPER" */; + buildPhases = ( + 82CB6E03BD9CE21DE81A405B /* [CP] Check Pods Manifest.lock */, + 25A741901D7ED45900473E02 /* Sources */, + 25A741911D7ED45900473E02 /* Frameworks */, + 25A741921D7ED45900473E02 /* Resources */, + 87A11139382E8ED739E3CEEC /* [CP] Embed Pods Frameworks */, + EBB7FE10DBFE07F521C6AD68 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "TableViewKit+VIPER"; + productName = "TableViewKit+VIPER"; + productReference = 25A741941D7ED45900473E02 /* TableViewKit+VIPER.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 25A7418C1D7ED45900473E02 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0730; + LastUpgradeCheck = 0800; + ORGANIZATIONNAME = "eDreams Odigeo"; + TargetAttributes = { + 25A741931D7ED45900473E02 = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; + }; + }; + }; + buildConfigurationList = 25A7418F1D7ED45900473E02 /* Build configuration list for PBXProject "TableViewKit+VIPER" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 25A7418B1D7ED45900473E02; + productRefGroup = 25A741951D7ED45900473E02 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 25A741931D7ED45900473E02 /* TableViewKit+VIPER */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 25A741921D7ED45900473E02 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 25A741A21D7ED45900473E02 /* LaunchScreen.storyboard in Resources */, + 25483FA11D7EFCFA002CF25E /* HelpCenterCell.xib in Resources */, + 25A7419F1D7ED45900473E02 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 82CB6E03BD9CE21DE81A405B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 87A11139382E8ED739E3CEEC /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TableViewKit+VIPER/Pods-TableViewKit+VIPER-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + EBB7FE10DBFE07F521C6AD68 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TableViewKit+VIPER/Pods-TableViewKit+VIPER-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 25A741901D7ED45900473E02 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 25A741AB1D7ED66600473E02 /* AboutModuleProtocols.swift in Sources */, + 25837C7A1D87FA39001EF4B8 /* MoreAboutCell.swift in Sources */, + 2579BBA01D805C9E003B9CDD /* HelpCenterSection.swift in Sources */, + 25A741AD1D7ED6D700473E02 /* AboutViewController.swift in Sources */, + 25483F9B1D7EDDE3002CF25E /* HelpCenterItem.swift in Sources */, + 25483F9F1D7EE0EF002CF25E /* MoreAboutItem.swift in Sources */, + 25D201691D7EDA410058C8A9 /* AboutPresenter.swift in Sources */, + 25D2016B1D7EDA680058C8A9 /* AboutWireFrame.swift in Sources */, + 2579BBA21D805CC2003B9CDD /* MoreAboutSection.swift in Sources */, + 25483F9D1D7EDE45002CF25E /* HelpCenterCell.swift in Sources */, + 25A741981D7ED45900473E02 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 25A741A01D7ED45900473E02 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 25A741A11D7ED45900473E02 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 25A741A41D7ED45900473E02 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 25A741A51D7ED45900473E02 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 25A741A71D7ED45900473E02 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9B851CD5029E3043F8A202FE /* Pods-TableViewKit+VIPER.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = "TableViewKit+VIPER/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.edreams.TableViewKit-VIPER"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 25A741A81D7ED45900473E02 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D42E2FAD5D3651C7A308A6E1 /* Pods-TableViewKit+VIPER.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = "TableViewKit+VIPER/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.edreams.TableViewKit-VIPER"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 25A7418F1D7ED45900473E02 /* Build configuration list for PBXProject "TableViewKit+VIPER" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 25A741A41D7ED45900473E02 /* Debug */, + 25A741A51D7ED45900473E02 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 25A741A61D7ED45900473E02 /* Build configuration list for PBXNativeTarget "TableViewKit+VIPER" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 25A741A71D7ED45900473E02 /* Debug */, + 25A741A81D7ED45900473E02 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 25A7418C1D7ED45900473E02 /* Project object */; +} diff --git a/Examples/Viper/TableViewKit+VIPER.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/Viper/TableViewKit+VIPER.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/Viper/TableViewKit+VIPER.xcworkspace/contents.xcworkspacedata b/Examples/Viper/TableViewKit+VIPER.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..3dce9d1 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutModuleProtocols.swift b/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutModuleProtocols.swift new file mode 100644 index 0000000..ac14fd5 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutModuleProtocols.swift @@ -0,0 +1,38 @@ +// +// AboutModuleProtocols.swift +// TableViewKit+VIPER +// +// Created by Nelson Dominguez Leon on 06/09/16. +// Copyright © 2016 eDreams Odigeo. All rights reserved. +// + +import Foundation +import UIKit + +protocol AboutWireFrameProtocol: class { + + func presentAboutViewModule(_ navigationController: UINavigationController) + func presentHelpCenter() +} + +protocol AboutPresenterProtocol: class { + + var router: AboutWireFrameProtocol? { get set } + var view: AboutViewControllerProtocol? { get set } + + func showHelpCenter() + + func showFaq() + func showContactUs() + func showTermsAndConditions() + func showFeedback() + func showShareApp() + func showRateApp() +} + +protocol AboutViewControllerProtocol: class { + + var presenter: AboutPresenterProtocol? { get set } + + func presentMessage(_ message: String, title: String) +} diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutPresenter.swift b/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutPresenter.swift new file mode 100644 index 0000000..4671c13 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutPresenter.swift @@ -0,0 +1,49 @@ +// +// AboutPresenter.swift +// TableViewKit+VIPER +// +// Created by Nelson Dominguez Leon on 06/09/16. +// Copyright © 2016 eDreams Odigeo. All rights reserved. +// + +import Foundation + +class AboutPresenter: AboutPresenterProtocol { + + var router: AboutWireFrameProtocol? + weak var view: AboutViewControllerProtocol? + + func showHelpCenter() { + router?.presentHelpCenter() + } + + func showFaq() { + let message = "FAQ not implemented" + view?.presentMessage(message, title: "Error") + } + + func showContactUs() { + let message = "Contact Us not implemented" + view?.presentMessage(message, title: "Error") + } + + func showTermsAndConditions() { + let message = "Terms and Conditions not implemented" + view?.presentMessage(message, title: "Error") + } + + func showFeedback() { + let message = "Feedback not implemented" + view?.presentMessage(message, title: "Error") + } + + func showShareApp() { + let message = "Share the app not implemented" + view?.presentMessage(message, title: "Error") + } + + func showRateApp() { + let message = "Rate the app not implemented" + view?.presentMessage(message, title: "Error") + } +} \ No newline at end of file diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutViewController.swift b/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutViewController.swift new file mode 100644 index 0000000..fc8a1db --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutViewController.swift @@ -0,0 +1,34 @@ +// +// AboutViewController.swift +// TableViewKit+VIPER +// +// Created by Nelson Dominguez Leon on 06/09/16. +// Copyright © 2016 eDreams Odigeo. All rights reserved. +// + +import Foundation +import UIKit +import TableViewKit + +class AboutViewController: UITableViewController, AboutViewControllerProtocol { + + var presenter: AboutPresenterProtocol? + var tableViewManager: TableViewManager? + + override func viewDidLoad() { + super.viewDidLoad() + + title = "Info" + + tableViewManager = TableViewManager(tableView: self.tableView) + tableViewManager?.sections.append(HelpCenterSection(presenter: presenter)) + tableViewManager?.sections.append(MoreAboutSection(presenter: presenter, manager: tableViewManager)) + } + + func presentMessage(_ message: String, title: String) { + + let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) + present(alertController, animated: true, completion: nil) + } +} diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutWireFrame.swift b/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutWireFrame.swift new file mode 100644 index 0000000..0f6de75 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/AboutWireFrame.swift @@ -0,0 +1,36 @@ +// +// AboutWireFrame.swift +// TableViewKit+VIPER +// +// Created by Nelson Dominguez Leon on 06/09/16. +// Copyright © 2016 eDreams Odigeo. All rights reserved. +// + +import Foundation +import UIKit +import SafariServices + +class AboutWireFrame: AboutWireFrameProtocol { + + fileprivate var viewController: AboutViewController? + + func presentAboutViewModule(_ navigationController: UINavigationController) { + + let presenter = AboutPresenter() + let view = AboutViewController(style: .grouped) + + // Connections + presenter.router = self + presenter.view = view + view.presenter = presenter + + navigationController.pushViewController(view, animated: true) + viewController = view + } + + func presentHelpCenter() { + + let webViewController = SFSafariViewController(url: URL(string: "https://www.edreams.com")!) + viewController?.navigationController?.pushViewController(webViewController, animated: true) + } +} diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterCell.swift b/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterCell.swift new file mode 100644 index 0000000..ac0896e --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterCell.swift @@ -0,0 +1,39 @@ +// +// HelpCenterCell.swift +// TableViewKit+VIPER +// +// Created by Nelson Dominguez Leon on 06/09/16. +// Copyright © 2016 eDreams Odigeo. All rights reserved. +// + +import Foundation +import TableViewKit + +class HelpCenterDrawer: CellDrawer { + + static var type: CellType = CellType.nib(UINib(nibName: String(describing: HelpCenterCell.self), bundle: Bundle.main), HelpCenterCell.self) + + static func draw(_ cell: UITableViewCell, with item: Any) { + + guard let helpCenterCell = cell as? HelpCenterCell, + let helpCenterItem = item as? HelpCenterItem + else { return } + + helpCenterCell.selectionStyle = .none + helpCenterCell.titleLabel.text = helpCenterItem.title + } +} + +class HelpCenterCell: UITableViewCell, ItemCompatible { + + public var item: Item? + + @IBOutlet var titleLabel: UILabel! + @IBOutlet var detailLabel: UILabel! + @IBOutlet var helpCenterButton: UIButton! + + @IBAction func helpCenterButtonSelected() { + guard let helpCenterItem = item as? HelpCenterItem else { return } + helpCenterItem.onHelpCenterButtonSelected?() + } +} diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterCell.xib b/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterCell.xib new file mode 100644 index 0000000..c49f3a0 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterCell.xib @@ -0,0 +1,97 @@ + + + + + + + + + + SourceSansPro-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterItem.swift b/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterItem.swift new file mode 100644 index 0000000..d0ac2af --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterItem.swift @@ -0,0 +1,25 @@ +// +// HelpCenterItem.swift +// TableViewKit+VIPER +// +// Created by Nelson Dominguez Leon on 06/09/16. +// Copyright © 2016 eDreams Odigeo. All rights reserved. +// + +import Foundation +import TableViewKit + +class HelpCenterItem: Item { + + var drawer: CellDrawer.Type = HelpCenterDrawer.self + var height: Height? = .dynamic(44.0) + + var title: String? + var subtitles: [String]? + var onHelpCenterButtonSelected: (() -> ())? + + init() { + title = "Find the answer to your questions" + subtitles = ["Check my booking status", "Change my flight", "Check my baggage allowance"] + } +} diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterSection.swift b/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterSection.swift new file mode 100644 index 0000000..fc02c80 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/HelpCenterSection.swift @@ -0,0 +1,29 @@ +// +// HelpCenterSection.swift +// TableViewKit+VIPER +// +// Created by Nelson Dominguez Leon on 07/09/16. +// Copyright © 2016 eDreams Odigeo. All rights reserved. +// + +import Foundation +import TableViewKit + +class HelpCenterSection: Section { + + var items: ObservableArray = [] + var header: HeaderFooterView = .title("How can we help you today?") + let presenter: AboutPresenterProtocol? + + required init(presenter: AboutPresenterProtocol?) { + + self.presenter = presenter + + let helpCenterItem = HelpCenterItem() + helpCenterItem.onHelpCenterButtonSelected = { + self.presenter?.showHelpCenter() + } + + items.append(helpCenterItem) + } +} diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutCell.swift b/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutCell.swift new file mode 100644 index 0000000..33602e0 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutCell.swift @@ -0,0 +1,23 @@ +// +// MoreAboutCell.swift +// TableViewKit+VIPER +// +// Created by Nelson Dominguez Leon on 13/09/16. +// Copyright © 2016 eDreams Odigeo. All rights reserved. +// + +import Foundation +import TableViewKit + +class MoreAboutDrawer: CellDrawer { + + static open var type = CellType.class(UITableViewCell.self) + + static open func draw(_ cell: UITableViewCell, with item: Any) { + + let item = item as! MoreAboutItem + + cell.accessoryType = .disclosureIndicator + cell.textLabel?.text = item.title + } +} diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutItem.swift b/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutItem.swift new file mode 100644 index 0000000..668876a --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutItem.swift @@ -0,0 +1,47 @@ +// +// MoreAboutItem.swift +// TableViewKit+VIPER +// +// Created by Nelson Dominguez Leon on 06/09/16. +// Copyright © 2016 eDreams Odigeo. All rights reserved. +// + +import Foundation +import TableViewKit + +enum MoreAboutItemType { + + case faq, contact, terms, feedback, share, rate + + func title() -> String { + switch self { + case .faq: + return "FAQ" + case .contact: + return "Contact Us" + case .terms: + return "Terms and Conditions" + case .feedback: + return "Send us your feedback" + case .share: + return "Share the app" + case .rate: + return "Rate the app" + } + } +} + +class MoreAboutItem: Item, Selectable { + + var type: MoreAboutItemType + var title: String? + + var drawer: CellDrawer.Type = MoreAboutDrawer.self + var onSelection: (Selectable) -> () = { _ in } + + init(type: MoreAboutItemType) { + + self.type = type + self.title = type.title() + } +} diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutSection.swift b/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutSection.swift new file mode 100644 index 0000000..4cfaf2e --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutSection.swift @@ -0,0 +1,50 @@ +// +// MoreAboutSection.swift +// TableViewKit+VIPER +// +// Created by Nelson Dominguez Leon on 07/09/16. +// Copyright © 2016 eDreams Odigeo. All rights reserved. +// + +import Foundation +import TableViewKit + +class MoreAboutSection: Section { + + var items: ObservableArray = [] + var header: HeaderFooterView = .title("More about eDreams") + let presenter: AboutPresenterProtocol? + weak var manager: TableViewManager? + + required init(presenter: AboutPresenterProtocol?, manager: TableViewManager?) { + + self.presenter = presenter + self.manager = manager + + let types: [MoreAboutItemType] = [.faq, .contact, .terms, .feedback, .share, .rate] + for type in types { + let moreAboutItem = MoreAboutItem(type: type) + moreAboutItem.onSelection = { item in + + switch type { + case .faq: + self.presenter?.showFaq() + case .contact: + self.presenter?.showContactUs() + case .terms: + self.presenter?.showTermsAndConditions() + case .feedback: + self.presenter?.showFeedback() + case .share: + self.presenter?.showShareApp() + case .rate: + self.presenter?.showRateApp() + } + + guard let manager = self.manager else { return } + item.deselect(in: manager, animated: true) + } + items.append(moreAboutItem) + } + } +} diff --git a/Examples/Viper/TableViewKit+VIPER/AppDelegate.swift b/Examples/Viper/TableViewKit+VIPER/AppDelegate.swift new file mode 100644 index 0000000..aa92514 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/AppDelegate.swift @@ -0,0 +1,55 @@ +// +// AppDelegate.swift +// TableViewKit+VIPER +// +// Created by Nelson Dominguez Leon on 06/09/16. +// Copyright © 2016 eDreams Odigeo. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + + let navigationController = UINavigationController() + + window = UIWindow(frame: UIScreen.main.bounds) + window?.backgroundColor = UIColor.white + window?.rootViewController = navigationController + window?.makeKeyAndVisible() + + let mainModule = AboutWireFrame() + mainModule.presentAboutViewModule(navigationController) + + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..118c98f --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/Contents.json b/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/help_Center_icon.imageset/Contents.json b/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/help_Center_icon.imageset/Contents.json new file mode 100644 index 0000000..2bc71a9 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/help_Center_icon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "help_Center_icon@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "help_Center_icon@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/help_Center_icon.imageset/help_Center_icon@2x.png b/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/help_Center_icon.imageset/help_Center_icon@2x.png new file mode 100644 index 0000000..001447b Binary files /dev/null and b/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/help_Center_icon.imageset/help_Center_icon@2x.png differ diff --git a/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/help_Center_icon.imageset/help_Center_icon@3x.png b/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/help_Center_icon.imageset/help_Center_icon@3x.png new file mode 100644 index 0000000..efbaa7a Binary files /dev/null and b/Examples/Viper/TableViewKit+VIPER/Assets.xcassets/help_Center_icon.imageset/help_Center_icon@3x.png differ diff --git a/Examples/Viper/TableViewKit+VIPER/Base.lproj/LaunchScreen.storyboard b/Examples/Viper/TableViewKit+VIPER/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..2e721e1 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/Viper/TableViewKit+VIPER/Info.plist b/Examples/Viper/TableViewKit+VIPER/Info.plist new file mode 100644 index 0000000..d39a4da --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/Info.plist @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Examples/Viper/TableViewKit+VIPER/LoginView.xib b/Examples/Viper/TableViewKit+VIPER/LoginView.xib new file mode 100644 index 0000000..e2aa674 --- /dev/null +++ b/Examples/Viper/TableViewKit+VIPER/LoginView.xib @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/External/Nimble b/External/Nimble new file mode 160000 index 0000000..fcc28b2 --- /dev/null +++ b/External/Nimble @@ -0,0 +1 @@ +Subproject commit fcc28b23f57b30382e5f182c238674694d7174cb diff --git a/Podfile b/Podfile deleted file mode 100755 index b530c48..0000000 --- a/Podfile +++ /dev/null @@ -1,10 +0,0 @@ -# Note: You must be using Cocoapods 1.0.0 or above - -use_frameworks! - -target :'TableViewKit' do -end - -target :'TableViewKitTests' do - pod 'Nimble' -end diff --git a/README.md b/README.md index 8229272..801a2c1 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,18 @@ -# TableViewKit - -[![Build Status](https://travis-ci.org/JensRavens/Interstellar.svg)](https://travis-ci.org/odigeoteam/TableViewKit) - -TableViewKit allows to manage the content of a `UITableView`. `Strategy` pattern for draw cells. +

+TableViewKit +

+

+ + + + + +

+ +Empowering `UITableView` with painless multi-type cell support and build-in automatic state transition animations ## Requirements -- Xcode 7 or higher +- Xcode 8 or higher - iOS 8.0 or higher ## Installation @@ -32,9 +39,3 @@ $ pod install - If you **found a bug**, open an issue. - If you **have a feature request**, open an issue. - If you **want to contribute**, submit a pull request. - -## Contact -iOS Mobile Team ios-dev@odigeo.com - -## License -TableViewKit is licensed under the `MIT` license. Check out the `LICENSE` file to learn more. diff --git a/TableViewKit.podspec b/TableViewKit.podspec index 1069dc6..8630e66 100644 --- a/TableViewKit.podspec +++ b/TableViewKit.podspec @@ -1,15 +1,13 @@ Pod::Spec.new do |s| s.name = "TableViewKit" - s.version = "0.5.0" - s.summary = "TableView Kit Layer" - s.description = "TableView Kit Layer" + s.version = "0.9.0" + s.summary = "Empowering UITableView with painless multi-type cell support and build-in automatic state transition animations" s.homepage = "http://www.edreamsodigeo.com/" - s.license = "Copyright (c) 2014 eDreams ODIGEO. All rights reserved" - s.author = { "iOS Mobile Team" => "ios-dev@odigeo.com" } + s.license = "MIT" + s.author = "TableViewKit Contributors" s.ios.deployment_target = "8.0" - s.source = { :path => '.' } + s.source = { :git => 'https://github.com/odigeoteam/TableViewKit.git', :tag => "v#{s.version}" } s.source_files = "TableViewKit/**/*.swift" - s.resource_bundles = { "TableViewKit" => "TableViewKit/Resources/*.*" } s.framework = "UIKit", "Foundation" s.requires_arc = true end diff --git a/TableViewKit.xcodeproj/project.pbxproj b/TableViewKit.xcodeproj/project.pbxproj index faca991..e200432 100644 --- a/TableViewKit.xcodeproj/project.pbxproj +++ b/TableViewKit.xcodeproj/project.pbxproj @@ -7,34 +7,29 @@ objects = { /* Begin PBXBuildFile section */ - 25F08BBD1D7427680033AD0E /* ValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F08BBC1D7427680033AD0E /* ValidatorTests.swift */; }; - 7892A1603117CFA582BFCAC3 /* Pods_TableViewKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA8FF610F26EA3E790F577BA /* Pods_TableViewKit.framework */; }; - AD0E465917E1381DFF2E904B /* Pods_TableViewKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5221BF34A600267BBA677E8E /* Pods_TableViewKitTests.framework */; }; - CC5CF11C1D839E71004DECB3 /* Diff.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC5CF11B1D839E71004DECB3 /* Diff.swift */; }; + 2564E61A1D88419B00A9DC3E /* TestRegisterNibCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2564E6191D88419B00A9DC3E /* TestRegisterNibCell.xib */; }; + 2564E61C1D88450F00A9DC3E /* TestRegisterHeaderFooterView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2564E61B1D88450F00A9DC3E /* TestRegisterHeaderFooterView.xib */; }; + 25837C781D87F819001EF4B8 /* ItemCompatible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25837C771D87F819001EF4B8 /* ItemCompatible.swift */; }; + CC5CF11C1D839E71004DECB3 /* ArrayDiff.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC5CF11B1D839E71004DECB3 /* ArrayDiff.swift */; }; CC97D76D1D741DC4009CDF9D /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC97D76C1D741DC4009CDF9D /* Selectable.swift */; }; + CC9D50E11D8496180010FCA3 /* TableViewDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC9D50E01D8496180010FCA3 /* TableViewDelegateTests.swift */; }; CCBE45601D72F69500414A64 /* TableViewKit.h in Headers */ = {isa = PBXBuildFile; fileRef = CCBE455F1D72F69500414A64 /* TableViewKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; CCBE45671D72F69500414A64 /* TableViewKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CCBE455C1D72F69400414A64 /* TableViewKit.framework */; }; CCBE456C1D72F69500414A64 /* TableViewKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE456B1D72F69500414A64 /* TableViewKitTests.swift */; }; - CCBE458F1D72F79C00414A64 /* ActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE45771D72F79C00414A64 /* ActionBar.swift */; }; CCBE45921D72F79C00414A64 /* NSIndexSet+Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE457B1D72F79C00414A64 /* NSIndexSet+Array.swift */; }; CCBE45931D72F79C00414A64 /* UITableView+Register.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE457C1D72F79C00414A64 /* UITableView+Register.swift */; }; - CCBE45971D72F79C00414A64 /* Rules.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE45831D72F79C00414A64 /* Rules.swift */; }; - CCBE45981D72F79C00414A64 /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE45841D72F79C00414A64 /* Validation.swift */; }; - CCBE45991D72F79C00414A64 /* ValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE45851D72F79C00414A64 /* ValidationError.swift */; }; - CCBE459A1D72F79C00414A64 /* Validator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE45861D72F79C00414A64 /* Validator.swift */; }; - CCBE459C1D72F79C00414A64 /* BaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE45891D72F79C00414A64 /* BaseCell.swift */; }; CCBE459D1D72F79C00414A64 /* CellDrawer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE458A1D72F79C00414A64 /* CellDrawer.swift */; }; CCBE459F1D72F79C00414A64 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE458C1D72F79C00414A64 /* Item.swift */; }; CCBE45A01D72F79C00414A64 /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE458D1D72F79C00414A64 /* Section.swift */; }; CCBE45A11D72F79C00414A64 /* TableViewManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBE458E1D72F79C00414A64 /* TableViewManager.swift */; }; + CCC749411D89AAD00030F140 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CCC749401D89AAD00030F140 /* Nimble.framework */; }; CCCAC1201D7D9E150001FC1D /* CellType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCAC11F1D7D9E150001FC1D /* CellType.swift */; }; CCCAC1221D7D9E440001FC1D /* HeaderFooterDrawer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCAC1211D7D9E440001FC1D /* HeaderFooterDrawer.swift */; }; CCCAC1241D7D9E6D0001FC1D /* HeaderFooterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCAC1231D7D9E6D0001FC1D /* HeaderFooterType.swift */; }; CCCAC1261D7D9EE00001FC1D /* HeaderFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCAC1251D7D9EE00001FC1D /* HeaderFooter.swift */; }; - CCCAC1281D7DA2D00001FC1D /* ImmutableMutableHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCAC1271D7DA2CF0001FC1D /* ImmutableMutableHeight.swift */; }; + CCCAC1281D7DA2D00001FC1D /* Height.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCAC1271D7DA2CF0001FC1D /* Height.swift */; }; CCDB8FAB1D83649B00E44A99 /* ObservableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDB8FAA1D83649B00E44A99 /* ObservableArray.swift */; }; CCE400F61D731B7300537715 /* TableViewDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE400F51D731B7300537715 /* TableViewDataSourceTests.swift */; }; - CCE400FA1D73321600537715 /* ActionBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE400F91D73321600537715 /* ActionBarTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -48,41 +43,33 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 08A270D360D8629E318EBF6E /* Pods-TableViewKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TableViewKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TableViewKit/Pods-TableViewKit.debug.xcconfig"; sourceTree = ""; }; - 25F08BBC1D7427680033AD0E /* ValidatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidatorTests.swift; sourceTree = ""; }; - 4B8E33116CC0B70461DC5ED6 /* Pods-TableViewKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TableViewKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TableViewKitTests/Pods-TableViewKitTests.debug.xcconfig"; sourceTree = ""; }; - 5221BF34A600267BBA677E8E /* Pods_TableViewKitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TableViewKitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - AA8FF610F26EA3E790F577BA /* Pods_TableViewKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TableViewKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - C6C8E0D2A241295B7A143FE9 /* Pods-TableViewKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TableViewKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-TableViewKitTests/Pods-TableViewKitTests.release.xcconfig"; sourceTree = ""; }; - CC5CF11B1D839E71004DECB3 /* Diff.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Diff.swift; sourceTree = ""; }; + 2564E6191D88419B00A9DC3E /* TestRegisterNibCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TestRegisterNibCell.xib; sourceTree = ""; }; + 2564E61B1D88450F00A9DC3E /* TestRegisterHeaderFooterView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TestRegisterHeaderFooterView.xib; sourceTree = ""; }; + 25837C771D87F819001EF4B8 /* ItemCompatible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ItemCompatible.swift; path = Protocols/ItemCompatible.swift; sourceTree = ""; }; + 4BCAD51C1D89A704002F3420 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = "External/Nimble/build/Debug-iphoneos/Nimble.framework"; sourceTree = ""; }; + CC5CF11B1D839E71004DECB3 /* ArrayDiff.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrayDiff.swift; sourceTree = ""; }; CC97D76C1D741DC4009CDF9D /* Selectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Selectable.swift; path = Protocols/Selectable.swift; sourceTree = ""; }; + CC9D50E01D8496180010FCA3 /* TableViewDelegateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewDelegateTests.swift; sourceTree = ""; }; CCBE455C1D72F69400414A64 /* TableViewKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TableViewKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CCBE455F1D72F69500414A64 /* TableViewKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TableViewKit.h; sourceTree = ""; }; CCBE45611D72F69500414A64 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CCBE45661D72F69500414A64 /* TableViewKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TableViewKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; CCBE456B1D72F69500414A64 /* TableViewKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewKitTests.swift; sourceTree = ""; }; CCBE456D1D72F69500414A64 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - CCBE45771D72F79C00414A64 /* ActionBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionBar.swift; sourceTree = ""; }; CCBE457B1D72F79C00414A64 /* NSIndexSet+Array.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSIndexSet+Array.swift"; sourceTree = ""; }; CCBE457C1D72F79C00414A64 /* UITableView+Register.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITableView+Register.swift"; sourceTree = ""; }; - CCBE45831D72F79C00414A64 /* Rules.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rules.swift; sourceTree = ""; }; - CCBE45841D72F79C00414A64 /* Validation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validation.swift; sourceTree = ""; }; - CCBE45851D72F79C00414A64 /* ValidationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationError.swift; sourceTree = ""; }; - CCBE45861D72F79C00414A64 /* Validator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validator.swift; sourceTree = ""; }; - CCBE45891D72F79C00414A64 /* BaseCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseCell.swift; sourceTree = ""; }; CCBE458A1D72F79C00414A64 /* CellDrawer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellDrawer.swift; sourceTree = ""; }; CCBE458C1D72F79C00414A64 /* Item.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Item.swift; path = Protocols/Item.swift; sourceTree = ""; }; CCBE458D1D72F79C00414A64 /* Section.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = ""; }; CCBE458E1D72F79C00414A64 /* TableViewManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewManager.swift; sourceTree = ""; }; + CCC749401D89AAD00030F140 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = "External/Nimble/build/Debug-iphoneos/Nimble.framework"; sourceTree = ""; }; CCCAC11F1D7D9E150001FC1D /* CellType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellType.swift; sourceTree = ""; }; CCCAC1211D7D9E440001FC1D /* HeaderFooterDrawer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderFooterDrawer.swift; sourceTree = ""; }; CCCAC1231D7D9E6D0001FC1D /* HeaderFooterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderFooterType.swift; sourceTree = ""; }; CCCAC1251D7D9EE00001FC1D /* HeaderFooter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HeaderFooter.swift; path = Protocols/HeaderFooter.swift; sourceTree = ""; }; - CCCAC1271D7DA2CF0001FC1D /* ImmutableMutableHeight.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImmutableMutableHeight.swift; sourceTree = ""; }; + CCCAC1271D7DA2CF0001FC1D /* Height.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Height.swift; sourceTree = ""; }; CCDB8FAA1D83649B00E44A99 /* ObservableArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableArray.swift; sourceTree = ""; }; CCE400F51D731B7300537715 /* TableViewDataSourceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewDataSourceTests.swift; sourceTree = ""; }; - CCE400F91D73321600537715 /* ActionBarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionBarTests.swift; sourceTree = ""; }; - D0E1FB244FF2B149DB428D40 /* Pods-TableViewKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TableViewKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-TableViewKit/Pods-TableViewKit.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -90,7 +77,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7892A1603117CFA582BFCAC3 /* Pods_TableViewKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -99,22 +85,20 @@ buildActionMask = 2147483647; files = ( CCBE45671D72F69500414A64 /* TableViewKit.framework in Frameworks */, - AD0E465917E1381DFF2E904B /* Pods_TableViewKitTests.framework in Frameworks */, + CCC749411D89AAD00030F140 /* Nimble.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 02ECBACBF25021B67BCCF813 /* Pods */ = { + 4BCAD51B1D89A703002F3420 /* Frameworks */ = { isa = PBXGroup; children = ( - 08A270D360D8629E318EBF6E /* Pods-TableViewKit.debug.xcconfig */, - D0E1FB244FF2B149DB428D40 /* Pods-TableViewKit.release.xcconfig */, - 4B8E33116CC0B70461DC5ED6 /* Pods-TableViewKitTests.debug.xcconfig */, - C6C8E0D2A241295B7A143FE9 /* Pods-TableViewKitTests.release.xcconfig */, + CCC749401D89AAD00030F140 /* Nimble.framework */, + 4BCAD51C1D89A704002F3420 /* Nimble.framework */, ); - name = Pods; + name = Frameworks; sourceTree = ""; }; CC97D76B1D741D8E009CDF9D /* Protocols */ = { @@ -124,27 +108,18 @@ CCCAC1251D7D9EE00001FC1D /* HeaderFooter.swift */, CCBE458C1D72F79C00414A64 /* Item.swift */, CC97D76C1D741DC4009CDF9D /* Selectable.swift */, + 25837C771D87F819001EF4B8 /* ItemCompatible.swift */, ); name = Protocols; sourceTree = ""; }; - CCAE56B0F1D6CA998E92E8A3 /* Frameworks */ = { - isa = PBXGroup; - children = ( - AA8FF610F26EA3E790F577BA /* Pods_TableViewKit.framework */, - 5221BF34A600267BBA677E8E /* Pods_TableViewKitTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; CCBE45521D72F69400414A64 = { isa = PBXGroup; children = ( CCBE455E1D72F69500414A64 /* TableViewKit */, CCBE456A1D72F69500414A64 /* TableViewKitTests */, CCBE455D1D72F69400414A64 /* Products */, - 02ECBACBF25021B67BCCF813 /* Pods */, - CCAE56B0F1D6CA998E92E8A3 /* Frameworks */, + 4BCAD51B1D89A703002F3420 /* Frameworks */, ); sourceTree = ""; }; @@ -164,15 +139,12 @@ CCCAC1291D7DA2DB0001FC1D /* Cell */, CCCAC12A1D7DA2E90001FC1D /* HeaderFooter */, CCBE45791D72F79C00414A64 /* Extensions */, - CCBE45821D72F79C00414A64 /* Validator */, - CCBE45761D72F79C00414A64 /* Controls */, CCDB8FAA1D83649B00E44A99 /* ObservableArray.swift */, - CCBE45891D72F79C00414A64 /* BaseCell.swift */, - CCCAC1271D7DA2CF0001FC1D /* ImmutableMutableHeight.swift */, + CCCAC1271D7DA2CF0001FC1D /* Height.swift */, CCBE458E1D72F79C00414A64 /* TableViewManager.swift */, CCBE455F1D72F69500414A64 /* TableViewKit.h */, CCBE45611D72F69500414A64 /* Info.plist */, - CC5CF11B1D839E71004DECB3 /* Diff.swift */, + CC5CF11B1D839E71004DECB3 /* ArrayDiff.swift */, ); path = TableViewKit; sourceTree = ""; @@ -180,23 +152,16 @@ CCBE456A1D72F69500414A64 /* TableViewKitTests */ = { isa = PBXGroup; children = ( - CCE400F91D73321600537715 /* ActionBarTests.swift */, + CC9D50E01D8496180010FCA3 /* TableViewDelegateTests.swift */, CCE400F51D731B7300537715 /* TableViewDataSourceTests.swift */, CCBE456B1D72F69500414A64 /* TableViewKitTests.swift */, - 25F08BBC1D7427680033AD0E /* ValidatorTests.swift */, CCBE456D1D72F69500414A64 /* Info.plist */, + 2564E6191D88419B00A9DC3E /* TestRegisterNibCell.xib */, + 2564E61B1D88450F00A9DC3E /* TestRegisterHeaderFooterView.xib */, ); path = TableViewKitTests; sourceTree = ""; }; - CCBE45761D72F79C00414A64 /* Controls */ = { - isa = PBXGroup; - children = ( - CCBE45771D72F79C00414A64 /* ActionBar.swift */, - ); - path = Controls; - sourceTree = ""; - }; CCBE45791D72F79C00414A64 /* Extensions */ = { isa = PBXGroup; children = ( @@ -206,17 +171,6 @@ path = Extensions; sourceTree = ""; }; - CCBE45821D72F79C00414A64 /* Validator */ = { - isa = PBXGroup; - children = ( - CCBE45831D72F79C00414A64 /* Rules.swift */, - CCBE45841D72F79C00414A64 /* Validation.swift */, - CCBE45851D72F79C00414A64 /* ValidationError.swift */, - CCBE45861D72F79C00414A64 /* Validator.swift */, - ); - path = Validator; - sourceTree = ""; - }; CCCAC1291D7DA2DB0001FC1D /* Cell */ = { isa = PBXGroup; children = ( @@ -253,12 +207,10 @@ isa = PBXNativeTarget; buildConfigurationList = CCBE45701D72F69500414A64 /* Build configuration list for PBXNativeTarget "TableViewKit" */; buildPhases = ( - 78E805C3C2C195FFA08ABCC3 /* [CP] Check Pods Manifest.lock */, CCBE45571D72F69400414A64 /* Sources */, CCBE45581D72F69400414A64 /* Frameworks */, CCBE45591D72F69400414A64 /* Headers */, CCBE455A1D72F69400414A64 /* Resources */, - E222C0F57807B3353D60F61A /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -273,12 +225,9 @@ isa = PBXNativeTarget; buildConfigurationList = CCBE45731D72F69500414A64 /* Build configuration list for PBXNativeTarget "TableViewKitTests" */; buildPhases = ( - 411E4A5183CAC8492B42A7D7 /* [CP] Check Pods Manifest.lock */, CCBE45621D72F69500414A64 /* Sources */, CCBE45631D72F69500414A64 /* Frameworks */, CCBE45641D72F69500414A64 /* Resources */, - E89F72306AD260BDE385DC1E /* [CP] Embed Pods Frameworks */, - 29F95B15EB6BD79C0DD59BD2 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -297,14 +246,16 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = odigeo; TargetAttributes = { CCBE455B1D72F69400414A64 = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; }; CCBE45651D72F69500414A64 = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; }; }; }; @@ -338,89 +289,13 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 2564E61C1D88450F00A9DC3E /* TestRegisterHeaderFooterView.xib in Resources */, + 2564E61A1D88419B00A9DC3E /* TestRegisterNibCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 29F95B15EB6BD79C0DD59BD2 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TableViewKitTests/Pods-TableViewKitTests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 411E4A5183CAC8492B42A7D7 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; - showEnvVarsInLog = 0; - }; - 78E805C3C2C195FFA08ABCC3 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; - showEnvVarsInLog = 0; - }; - E222C0F57807B3353D60F61A /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TableViewKit/Pods-TableViewKit-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - E89F72306AD260BDE385DC1E /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-TableViewKitTests/Pods-TableViewKitTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ CCBE45571D72F69400414A64 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -428,17 +303,12 @@ files = ( CCBE45A01D72F79C00414A64 /* Section.swift in Sources */, CCBE45921D72F79C00414A64 /* NSIndexSet+Array.swift in Sources */, - CCBE45991D72F79C00414A64 /* ValidationError.swift in Sources */, - CCBE45971D72F79C00414A64 /* Rules.swift in Sources */, CC97D76D1D741DC4009CDF9D /* Selectable.swift in Sources */, - CCCAC1281D7DA2D00001FC1D /* ImmutableMutableHeight.swift in Sources */, - CC5CF11C1D839E71004DECB3 /* Diff.swift in Sources */, - CCBE459C1D72F79C00414A64 /* BaseCell.swift in Sources */, + CCCAC1281D7DA2D00001FC1D /* Height.swift in Sources */, + CC5CF11C1D839E71004DECB3 /* ArrayDiff.swift in Sources */, + 25837C781D87F819001EF4B8 /* ItemCompatible.swift in Sources */, CCCAC1201D7D9E150001FC1D /* CellType.swift in Sources */, CCDB8FAB1D83649B00E44A99 /* ObservableArray.swift in Sources */, - CCBE458F1D72F79C00414A64 /* ActionBar.swift in Sources */, - CCBE459A1D72F79C00414A64 /* Validator.swift in Sources */, - CCBE45981D72F79C00414A64 /* Validation.swift in Sources */, CCCAC1261D7D9EE00001FC1D /* HeaderFooter.swift in Sources */, CCCAC1241D7D9E6D0001FC1D /* HeaderFooterType.swift in Sources */, CCBE45A11D72F79C00414A64 /* TableViewManager.swift in Sources */, @@ -453,8 +323,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 25F08BBD1D7427680033AD0E /* ValidatorTests.swift in Sources */, - CCE400FA1D73321600537715 /* ActionBarTests.swift in Sources */, + CC9D50E11D8496180010FCA3 /* TableViewDelegateTests.swift in Sources */, CCE400F61D731B7300537715 /* TableViewDataSourceTests.swift in Sources */, CCBE456C1D72F69500414A64 /* TableViewKitTests.swift in Sources */, ); @@ -485,8 +354,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -514,6 +385,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -534,8 +406,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -555,6 +429,8 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.3; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -564,9 +440,9 @@ }; CCBE45711D72F69500414A64 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08A270D360D8629E318EBF6E /* Pods-TableViewKit.debug.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -578,15 +454,14 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 2.3; }; name = Debug; }; CCBE45721D72F69500414A64 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0E1FB244FF2B149DB428D40 /* Pods-TableViewKit.release.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -597,31 +472,26 @@ PRODUCT_BUNDLE_IDENTIFIER = com.odigeo.TableViewKit; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 2.3; }; name = Release; }; CCBE45741D72F69500414A64 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4B8E33116CC0B70461DC5ED6 /* Pods-TableViewKitTests.debug.xcconfig */; buildSettings = { INFOPLIST_FILE = TableViewKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.odigeo.TableViewKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; }; name = Debug; }; CCBE45751D72F69500414A64 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C6C8E0D2A241295B7A143FE9 /* Pods-TableViewKitTests.release.xcconfig */; buildSettings = { INFOPLIST_FILE = TableViewKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.odigeo.TableViewKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; }; name = Release; }; diff --git a/TableViewKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TableViewKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..3f76a7e --- /dev/null +++ b/TableViewKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TableViewKit.xcodeproj/project.xcworkspace/xcshareddata/TableViewKit.xcscmblueprint b/TableViewKit.xcodeproj/project.xcworkspace/xcshareddata/TableViewKit.xcscmblueprint new file mode 100644 index 0000000..5ab119c --- /dev/null +++ b/TableViewKit.xcodeproj/project.xcworkspace/xcshareddata/TableViewKit.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "230D7996022BCE2DADCFFB71B8CC4F014EBD35F5", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "95438028B10BBB846574013D29F154A00556A9D1" : 9223372036854775807, + "230D7996022BCE2DADCFFB71B8CC4F014EBD35F5" : 9223372036854775807 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "2845FBB4-2F98-4013-889C-B218B59A86F1", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "95438028B10BBB846574013D29F154A00556A9D1" : "TableViewKit\/External\/Nimble\/", + "230D7996022BCE2DADCFFB71B8CC4F014EBD35F5" : "TableViewKit\/" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "TableViewKit", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "TableViewKit.xcodeproj", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/odigeoteam\/TableViewKit", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "230D7996022BCE2DADCFFB71B8CC4F014EBD35F5" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "git:\/\/github.com\/Quick\/Nimble.git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "95438028B10BBB846574013D29F154A00556A9D1" + } + ] +} \ No newline at end of file diff --git a/TableViewKit.xcodeproj/xcshareddata/xcschemes/TableViewKit.xcscheme b/TableViewKit.xcodeproj/xcshareddata/xcschemes/TableViewKit.xcscheme index b2c3fa2..2deb5a9 100644 --- a/TableViewKit.xcodeproj/xcshareddata/xcschemes/TableViewKit.xcscheme +++ b/TableViewKit.xcodeproj/xcshareddata/xcschemes/TableViewKit.xcscheme @@ -1,6 +1,6 @@ + + + + + + diff --git a/TableViewKit.xcworkspace/xcshareddata/TableViewKit.xcscmblueprint b/TableViewKit.xcworkspace/xcshareddata/TableViewKit.xcscmblueprint new file mode 100644 index 0000000..5ae230b --- /dev/null +++ b/TableViewKit.xcworkspace/xcshareddata/TableViewKit.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "230D7996022BCE2DADCFFB71B8CC4F014EBD35F5", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "95438028B10BBB846574013D29F154A00556A9D1" : 9223372036854775807, + "230D7996022BCE2DADCFFB71B8CC4F014EBD35F5" : 9223372036854775807 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "336302B4-D044-442B-AD6A-CD7A7F54790E", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "95438028B10BBB846574013D29F154A00556A9D1" : "TableViewKit\/External\/Nimble\/", + "230D7996022BCE2DADCFFB71B8CC4F014EBD35F5" : "TableViewKit\/" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "TableViewKit", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "TableViewKit.xcworkspace", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/odigeoteam\/TableViewKit", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "230D7996022BCE2DADCFFB71B8CC4F014EBD35F5" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "git:\/\/github.com\/Quick\/Nimble.git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "95438028B10BBB846574013D29F154A00556A9D1" + } + ] +} \ No newline at end of file diff --git a/TableViewKit/Diff.swift b/TableViewKit/ArrayDiff.swift similarity index 70% rename from TableViewKit/Diff.swift rename to TableViewKit/ArrayDiff.swift index 9d02251..e9462ba 100644 --- a/TableViewKit/Diff.swift +++ b/TableViewKit/ArrayDiff.swift @@ -11,10 +11,10 @@ import Foundation struct Diff { public var inserts: [Int] public var deletes: [Int] + public var moves: [(Int, Int)] } - -class DiffIterator : GeneratorType { +class DiffIterator : IteratorProtocol { struct Coordinates { var x: Int var y: Int @@ -50,26 +50,35 @@ class DiffIterator : GeneratorType { } } -class DiffSequence : SequenceType { +class DiffSequence : Sequence { private let matrix: [[Int]] init(matrix: [[Int]]){ self.matrix = matrix } - func generate() -> DiffIterator { + func makeIterator() -> DiffIterator { return DiffIterator(matrix: matrix) } } - - extension Array { static func diff(between x: [Element], and y: [Element], where predicate: Predicate) -> Diff { - var matrix = [[Int]](count: x.count+1, repeatedValue: [Int](count: y.count+1, repeatedValue: 0)) - for (i, xElem) in x.enumerate() { - for (j, yElem) in y.enumerate() { + var x = x + let moves = y.enumerated().flatMap { (toIndex, element) -> (Int, Int)? in + guard let fromIndex = x.index(where: { predicate($0, element) }), + fromIndex != toIndex else { return nil } + + x.remove(at: fromIndex) + x.insert(element, at: (toIndex >= x.count) ? x.count : toIndex) + + return (fromIndex, toIndex) + } + + var matrix = [[Int]](repeating: [Int](repeating: 0, count: y.count+1), count: x.count+1) + for (i, xElem) in x.enumerated() { + for (j, yElem) in y.enumerated() { if predicate(xElem, yElem) { matrix[i+1][j+1] = matrix[i][j] + 1 } else { @@ -82,16 +91,15 @@ extension Array { let inserts: [Int] = changes.flatMap { change -> [Int] in guard case .inserts(let array) = change else { return [] } return array - }.sort { $0 > $1 } + }.sorted { $0 > $1 } let deletes: [Int] = changes.flatMap { change -> [Int] in guard case .deletes(let array) = change else { return [] } return array - }.sort { $0 < $1 } + }.sorted { $0 < $1 } - return Diff(inserts: inserts, deletes: deletes) + return Diff(inserts: inserts, deletes: deletes, moves: moves) } - } diff --git a/TableViewKit/BaseCell.swift b/TableViewKit/BaseCell.swift deleted file mode 100644 index 35351ae..0000000 --- a/TableViewKit/BaseCell.swift +++ /dev/null @@ -1,154 +0,0 @@ -// -// TableViewCell.swift -// TableViewKit -// -// Created by Nelson Dominguez Leon on 08/06/16. -// Copyright © 2016 ODIGEO. All rights reserved. -// - -import Foundation -import UIKit - -public class BaseCell: UITableViewCell { - - // MARK: Public - - weak public var tableViewManager: TableViewManager! - public var item: Item? - - public var responder: UIResponder? - - public var actionBar: ActionBar! - - - // MARK: Constructors - - public required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - commonInit() - } - - public override required init(style: UITableViewCellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - commonInit() - } - - public override func awakeFromNib() { - super.awakeFromNib() - commonInit() - } - - // MARK: Configure - - public func commonInit() { - actionBar = ActionBar(delegate: self) - } - - public override func canBecomeFirstResponder() -> Bool { - guard let responder = responder else { return false } - return responder.canBecomeFirstResponder() - } - - public override func becomeFirstResponder() -> Bool { - guard let responder = responder else { return false } - return responder.becomeFirstResponder() - } - - public override func isFirstResponder() -> Bool { - guard let responder = responder else { return false } - return responder.isFirstResponder() - } - -} - -extension BaseCell: ActionBarDelegate { - - private func indexPathForPreviousResponderInSectionIndex(sectionIndex: Int) -> NSIndexPath? { - - guard let item = self.item else { return nil } - - let section = tableViewManager.sections[sectionIndex] - let indexInSection = section === item.section(inManager: tableViewManager) ? section.items.indexOf(item) : section.items.count - - guard indexInSection > 0 else { return nil } - - for itemIndex in (0 ..< indexInSection!).reverse() { - let previousItem = section.items[itemIndex] - guard let indexPath = previousItem.indexPath(inManager: tableViewManager), - let cell = tableViewManager.tableView.cellForRowAtIndexPath(indexPath) - else { continue } - if cell.canBecomeFirstResponder() { - return indexPath - } - - } - - return nil - } - - private func indexPathForPreviousResponder() -> NSIndexPath? { - guard let sectionIndex = item?.indexPath(inManager: tableViewManager)?.section else { return nil } - - - for index in (0 ... sectionIndex).reverse() { - if let indexPath = indexPathForPreviousResponderInSectionIndex(index) { - return indexPath - } - } - - return nil - } - - private func indexPathForNextResponderInSectionIndex(sectionIndex: Int) -> NSIndexPath? { - - let section = tableViewManager.sections[sectionIndex] - let indexInSection = section === item!.section(inManager: tableViewManager) ? section.items.indexOf(item!) : -1 - - for itemIndex in indexInSection! + 1 ..< section.items.count { - let nextItem = section.items[itemIndex] - guard let indexPath = nextItem.indexPath(inManager: tableViewManager), - let cell = tableViewManager.tableView.cellForRowAtIndexPath(indexPath) - else { continue } - if cell.canBecomeFirstResponder() { - return indexPath - } - } - - return nil - } - - private func indexPathForNextResponder() -> NSIndexPath? { - guard let sectionIndex = item?.indexPath(inManager: tableViewManager)?.section else { return nil } - - for index in sectionIndex ... tableViewManager.sections.count - 1 { - if let indexPath = indexPathForNextResponderInSectionIndex(index) { - return indexPath - } - } - - return nil - } - - private func indexPathForResponder(forDirection direction: Direction) -> NSIndexPath? { - - switch direction { - case .next: - return indexPathForNextResponder() - case .previous: - return indexPathForPreviousResponder() - } - } - - public func actionBar(actionBar: ActionBar, direction: Direction) -> NSIndexPath? { - guard let indexPath = indexPathForResponder(forDirection: direction) else { return nil } - tableViewManager.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Top, animated: true) - - let cell = tableViewManager.tableView.cellForRowAtIndexPath(indexPath) as! BaseCell - cell.becomeFirstResponder() - return indexPath - } - - public func actionBar(actionBar: ActionBar, doneButtonPressed doneButtonItem: UIBarButtonItem) { - endEditing(true) - } -} diff --git a/TableViewKit/CellDrawer.swift b/TableViewKit/CellDrawer.swift index 0e54a60..e49bec1 100644 --- a/TableViewKit/CellDrawer.swift +++ b/TableViewKit/CellDrawer.swift @@ -11,17 +11,22 @@ import UIKit public protocol CellDrawer { - static var cellType: CellType { get } - static func cell(inManager manager: TableViewManager, withItem item: Item, forIndexPath: NSIndexPath) -> BaseCell - static func draw(cell: BaseCell, withItem item: Any) + static var type: CellType { get } + static func cell(in manager: TableViewManager, with item: Item, for: IndexPath) -> UITableViewCell + static func draw(_ cell: UITableViewCell, with item: Any) } public extension CellDrawer { - static func cell(inManager manager: TableViewManager, withItem item: Item, forIndexPath indexPath: NSIndexPath) -> BaseCell { - let cell = manager.tableView.dequeueReusableCellWithIdentifier(self.cellType.reusableIdentifier, forIndexPath: indexPath) as! BaseCell - cell.tableViewManager = manager - cell.item = item + + static func cell(in manager: TableViewManager, with item: Item, for indexPath: IndexPath) -> UITableViewCell { + + let cell = manager.tableView.dequeueReusableCell(withIdentifier: self.type.reusableIdentifier, for: indexPath) + + if let cell = cell as? ItemCompatible { + cell.item = item + } + return cell } } diff --git a/TableViewKit/CellType.swift b/TableViewKit/CellType.swift index 6683cb6..64327df 100644 --- a/TableViewKit/CellType.swift +++ b/TableViewKit/CellType.swift @@ -10,23 +10,18 @@ import Foundation public enum CellType { - case Nib(UINib, UITableViewCell.Type) - case Class(UITableViewCell.Type) + case nib(UINib, UITableViewCell.Type) + case `class`(UITableViewCell.Type) public var reusableIdentifier: String { - switch self { - case .Class(let cellClass): - return String(cellClass) - case .Nib(_, let cellClass): - return String(cellClass) - } + return String(describing: cellClass) } public var cellClass: UITableViewCell.Type { switch self { - case .Class(let cellClass): + case .class(let cellClass): return cellClass - case .Nib(_, let cellClass): + case .nib(_, let cellClass): return cellClass } } diff --git a/TableViewKit/Extensions/NSIndexSet+Array.swift b/TableViewKit/Extensions/NSIndexSet+Array.swift index a81ab35..3f35ead 100644 --- a/TableViewKit/Extensions/NSIndexSet+Array.swift +++ b/TableViewKit/Extensions/NSIndexSet+Array.swift @@ -8,12 +8,11 @@ import Foundation -extension NSIndexSet { - - convenience init(_ array: [Int]) { +extension IndexSet { + init(_ array: [Int]) { let mutable = NSMutableIndexSet() - array.forEach {mutable.addIndex($0)} - self.init(indexSet: mutable) + array.forEach {mutable.add($0)} + self.init(mutable) } } diff --git a/TableViewKit/Extensions/UITableView+Register.swift b/TableViewKit/Extensions/UITableView+Register.swift index 25d8849..fb7ed1a 100644 --- a/TableViewKit/Extensions/UITableView+Register.swift +++ b/TableViewKit/Extensions/UITableView+Register.swift @@ -9,27 +9,27 @@ import UIKit public extension UITableView { - public func register(type type: CellType, bundle: NSBundle? = nil) { + public func register(_ type: CellType) { switch type { - case .Class(let cellClass): - registerClass(cellClass, forCellReuseIdentifier: type.reusableIdentifier) - case .Nib(let nib, _): - registerNib(nib, forCellReuseIdentifier: type.reusableIdentifier) + case .class(let cellClass): + self.register(cellClass, forCellReuseIdentifier: type.reusableIdentifier) + case .nib(let nib, _): + self.register(nib, forCellReuseIdentifier: type.reusableIdentifier) } } - public func register(type type: HeaderFooterType, bundle: NSBundle? = nil) { + public func register(_ type: HeaderFooterType) { switch type { - case .Class(let cellClass): - registerClass(cellClass, forHeaderFooterViewReuseIdentifier: type.reusableIdentifier) - case .Nib(let nib, _): - registerNib(nib, forHeaderFooterViewReuseIdentifier: type.reusableIdentifier) + case .class(let cellClass): + self.register(cellClass, forHeaderFooterViewReuseIdentifier: type.reusableIdentifier) + case .nib(let nib, _): + self.register(nib, forHeaderFooterViewReuseIdentifier: type.reusableIdentifier) } } - func moveRows(at indexPaths: [NSIndexPath], to newIndexPaths: [NSIndexPath]) { - for (index, _) in indexPaths.enumerate() { - moveRowAtIndexPath(indexPaths[index], toIndexPath: newIndexPaths[index]) + func moveRows(at indexPaths: [IndexPath], to newIndexPaths: [IndexPath]) { + for (index, _) in indexPaths.enumerated() { + moveRow(at: indexPaths[index], to: newIndexPaths[index]) } } diff --git a/TableViewKit/HeaderFooterDrawer.swift b/TableViewKit/HeaderFooterDrawer.swift index fd6fd19..eef6cd2 100644 --- a/TableViewKit/HeaderFooterDrawer.swift +++ b/TableViewKit/HeaderFooterDrawer.swift @@ -9,16 +9,31 @@ import Foundation import UIKit + +/// A type that can draw either a header or a footer public protocol HeaderFooterDrawer { - static var headerFooterType: HeaderFooterType { get } - static func view(inManager manager: TableViewManager, withItem item: HeaderFooter) -> UITableViewHeaderFooterView - static func draw(view: UITableViewHeaderFooterView, withItem item: Any) + /// Define the `type` of the header/footer + static var type: HeaderFooterType { get } + + /// Draw the `view` using the `item` + /// + /// - parameter view: The header/footer `view` that must be drawn + /// - parameter item: The header/footer `item` that generated the drawer + static func draw(_ view: UITableViewHeaderFooterView, with item: Any) + + /// Returns the header/footer view from the `manager` + /// + /// - parameter manager: The `manager` where the header/footer came from + /// - parameter item: The header/footer `item` + /// + /// - returns: The header/footer view + static func view(in manager: TableViewManager, with item: HeaderFooter) -> UITableViewHeaderFooterView } public extension HeaderFooterDrawer { - static func view(inManager manager: TableViewManager, withItem item: HeaderFooter) -> UITableViewHeaderFooterView { - return manager.tableView.dequeueReusableHeaderFooterViewWithIdentifier(self.headerFooterType.reusableIdentifier)! + static func view(in manager: TableViewManager, with item: HeaderFooter) -> UITableViewHeaderFooterView { + return manager.tableView.dequeueReusableHeaderFooterView(withIdentifier: self.type.reusableIdentifier)! } } diff --git a/TableViewKit/HeaderFooterType.swift b/TableViewKit/HeaderFooterType.swift index 9993239..a5fe88b 100644 --- a/TableViewKit/HeaderFooterType.swift +++ b/TableViewKit/HeaderFooterType.swift @@ -10,23 +10,18 @@ import Foundation public enum HeaderFooterType { - case Nib(UINib, UITableViewHeaderFooterView.Type) - case Class(UITableViewHeaderFooterView.Type) + case nib(UINib, UITableViewHeaderFooterView.Type) + case `class`(UITableViewHeaderFooterView.Type) public var reusableIdentifier: String { - switch self { - case .Class(let cellClass): - return String(cellClass) - case .Nib(_, let cellClass): - return String(cellClass) - } + return String(describing: cellClass) } public var cellClass: UITableViewHeaderFooterView.Type { switch self { - case .Class(let cellClass): + case .class(let cellClass): return cellClass - case .Nib(_, let cellClass): + case .nib(_, let cellClass): return cellClass } } diff --git a/TableViewKit/Height.swift b/TableViewKit/Height.swift new file mode 100644 index 0000000..4251cc7 --- /dev/null +++ b/TableViewKit/Height.swift @@ -0,0 +1,33 @@ +// +// ImmutableMutableHeight.swift +// TableViewKit +// +// Created by Alfredo Delli Bovi on 05/09/16. +// Copyright © 2016 odigeo. All rights reserved. +// + +import Foundation + +public enum Height { + case dynamic(CGFloat) + case `static`(CGFloat) + + + internal func estimated() -> CGFloat { + switch self { + case .static(_): + return 0.0 + case .dynamic(let value): + return value + } + } + + internal func height() -> CGFloat { + switch self { + case .static(let value): + return value + case .dynamic(_): + return UITableViewAutomaticDimension + } + } +} diff --git a/TableViewKit/ImmutableMutableHeight.swift b/TableViewKit/ImmutableMutableHeight.swift deleted file mode 100644 index c722ab5..0000000 --- a/TableViewKit/ImmutableMutableHeight.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// ImmutableMutableHeight.swift -// TableViewKit -// -// Created by Alfredo Delli Bovi on 05/09/16. -// Copyright © 2016 odigeo. All rights reserved. -// - -import Foundation - -public enum ImmutableMutableHeight { - case immutable(CGFloat) - case mutable(CGFloat) -} diff --git a/TableViewKit/ObservableArray.swift b/TableViewKit/ObservableArray.swift index c7c7137..e5648e1 100644 --- a/TableViewKit/ObservableArray.swift +++ b/TableViewKit/ObservableArray.swift @@ -19,100 +19,83 @@ enum ArrayChanges { case endUpdates } -public struct ObservableArray: ArrayLiteralConvertible, CollectionType, MutableCollectionType, RangeReplaceableCollectionType { +public struct ObservableArray: ExpressibleByArrayLiteral, Collection, MutableCollection, RangeReplaceableCollection { public typealias Element = T - private var _array: [T] { + private var array: [T] { willSet { callback?(.beginUpdates) } didSet { - let newArray = _array - var oldArray = oldValue - _arraySet(oldArray, newArray: newArray) - callback?(.endUpdates) - } - } - - private func _arraySet(oldArray: [T], newArray: [T]) { - var oldArray = oldArray - let moves = newArray.enumerate().flatMap { (toIndex, element) -> (Int, Int)? in - let anyElement = element as! AnyObject - guard let fromIndex = oldArray.indexOf({ $0 as! AnyObject === anyElement }) where - fromIndex != toIndex else { return nil } + let newArray = array as [AnyObject] + let oldArray = oldValue as [AnyObject] + + let diff = Array.diff(between: oldArray, and: newArray, where: { lhs, rhs in + let lhs = lhs as AnyObject + let rhs = rhs as AnyObject + return lhs === rhs + }) - oldArray.removeAtIndex(fromIndex) - if (toIndex >= oldArray.count) { - oldArray.append(element) - } else { - oldArray.insert(element, atIndex: toIndex) - } - return (fromIndex, toIndex) - } - - let equals: Predicate = { lhs, rhs in - let lhs = lhs as! AnyObject - let rhs = rhs as! AnyObject - return lhs === rhs + callback?(.moves(diff.moves)) + callback?(.deletes(diff.deletes)) + callback?(.inserts(diff.inserts)) + callback?(.endUpdates) } - let diff = Array.diff(between: oldArray, and: newArray, where: equals) - let deletes = diff.deletes - let inserts = diff.inserts - - callback?(.moves(moves)) - callback?(.deletes(deletes)) - callback?(.inserts(inserts)) } var callback: ((ArrayChanges) -> ())? public init() { - self._array = [] + self.array = [] } public init(array: [T]) { - self._array = array + self.array = array } public init(arrayLiteral elements: Element...) { - self._array = elements + self.array = elements } - public func generate() -> Array.Generator { - return _array.generate() + public func makeIterator() -> Array.Iterator { + return array.makeIterator() } public var startIndex: Int { - return _array.startIndex + return array.startIndex } public var endIndex: Int { - return _array.endIndex + return array.endIndex + } + + public func index(after i: Int) -> Int { + return array.index(after: i) } public var isEmpty: Bool { - return _array.isEmpty + return array.isEmpty } public var count: Int { - return _array.count + return array.count } public subscript(index: Int) -> T { get { - return _array[index] + return array[index] } set { - _array[index] = newValue + array[index] = newValue } } - - public mutating func replaceRange(_ subrange: Range, with newElements: C) { - _array.replaceRange(subrange, with: newElements) + + public mutating func replaceSubrange(_ subrange: Range, with newElements: C) where C : Collection, C.Iterator.Element == T { + array.replaceSubrange(subrange, with: newElements) } public mutating func replace(with array: [T]) { - _array = array + self.array = array } } diff --git a/TableViewKit/Protocols/HeaderFooter.swift b/TableViewKit/Protocols/HeaderFooter.swift index 4f2176d..9bcb247 100644 --- a/TableViewKit/Protocols/HeaderFooter.swift +++ b/TableViewKit/Protocols/HeaderFooter.swift @@ -8,7 +8,13 @@ import Foundation -public enum HeaderFooterView: NilLiteralConvertible, Equatable { +/// A type for a header or a footer that rapresent, if its' present, +/// if it's a simple `title` or if it's a custom `view` +/// +/// - title: A simple `title` +/// - view: A custom `view` +/// - none: A empty header/footer +public enum HeaderFooterView: ExpressibleByNilLiteral { case title(String) case view(HeaderFooter) case none @@ -17,20 +23,9 @@ public enum HeaderFooterView: NilLiteralConvertible, Equatable { self = .none } } -public func == (lhs: HeaderFooterView, rhs: HeaderFooterView) -> Bool { - switch (lhs, rhs) { - case (.none, .none): - return true - case (.view(let lhs), .view(let rhs)): - return lhs === rhs - case (.title(let lhs), .title(let rhs)): - return lhs == rhs - default: - return false - } -} +/// A type that it's associated to header/footer drawer public protocol HeaderFooter: class { var drawer: HeaderFooterDrawer.Type { get } - var height: ImmutableMutableHeight? { get } + var height: Height? { get } } diff --git a/TableViewKit/Protocols/Item.swift b/TableViewKit/Protocols/Item.swift index ffd697a..879694a 100644 --- a/TableViewKit/Protocols/Item.swift +++ b/TableViewKit/Protocols/Item.swift @@ -9,55 +9,47 @@ import Foundation public protocol Item: class { + var drawer: CellDrawer.Type { get } + var height: Height? { get } - var height: ImmutableMutableHeight? { get } + func indexPath(in manager: TableViewManager) -> IndexPath? + func section(in manager: TableViewManager) -> Section? - func indexPath(inManager manager: TableViewManager) -> NSIndexPath? - func section(inManager manager: TableViewManager) -> Section? - - func reloadRow(inManager manager: TableViewManager, withAnimation animation: UITableViewRowAnimation) - func deleteRow(inManager manager: TableViewManager, withAnimation animation: UITableViewRowAnimation) + func reload(in manager: TableViewManager, with animation: UITableViewRowAnimation) } extension Item { - public var height: ImmutableMutableHeight? { - return .mutable(44.0) + public var height: Height? { + return .dynamic(44.0) } - public func section(inManager manager: TableViewManager) -> Section? { - guard let indexPath = self.indexPath(inManager: manager) else { return nil } + public func section(in manager: TableViewManager) -> Section? { + guard let indexPath = self.indexPath(in: manager) else { return nil } return manager.sections[indexPath.section] } - public func indexPath(inManager manager: TableViewManager) -> NSIndexPath? { + public func indexPath(in manager: TableViewManager) -> IndexPath? { for section in manager.sections { guard - let sectionIndex = section.index(inManager: manager), - let rowIndex = section.items.indexOf(self) else { continue } - return NSIndexPath(forRow: rowIndex, inSection: sectionIndex) + let sectionIndex = section.index(in: manager), + let rowIndex = section.items.index(of: self) else { continue } + return IndexPath(row: rowIndex, section: sectionIndex) } return nil } - public func reloadRow(inManager manager: TableViewManager, withAnimation animation: UITableViewRowAnimation) { - - if let itemIndexPath = indexPath(inManager: manager) { - manager.tableView.reloadRowsAtIndexPaths([itemIndexPath], withRowAnimation: animation) - } + public func reload(in manager: TableViewManager, with animation: UITableViewRowAnimation = .automatic) { + guard let indexPath = self.indexPath(in: manager) else { return } + let section = manager.sections[indexPath.section] + section.items.callback?(.updates([indexPath.row])) } - public func deleteRow(inManager manager: TableViewManager, withAnimation animation: UITableViewRowAnimation) { - - if let itemIndexPath = indexPath(inManager: manager) { - manager.tableView.deleteRowsAtIndexPaths([itemIndexPath], withRowAnimation: animation) - } - } } -extension CollectionType where Generator.Element == Item { - func indexOf(element: Generator.Element) -> Index? { - return indexOf({ $0 === element }) +public extension Collection where Iterator.Element == Item { + func index(of element: Iterator.Element) -> Index? { + return index(where: { $0 === element }) } } diff --git a/TableViewKit/Protocols/ItemCompatible.swift b/TableViewKit/Protocols/ItemCompatible.swift new file mode 100644 index 0000000..fe367c4 --- /dev/null +++ b/TableViewKit/Protocols/ItemCompatible.swift @@ -0,0 +1,14 @@ +// +// Cell.swift +// TableViewKit +// +// Created by Nelson Dominguez Leon on 13/09/16. +// Copyright © 2016 odigeo. All rights reserved. +// + +import Foundation + +public protocol ItemCompatible: class { + + var item: Item? { get set } +} diff --git a/TableViewKit/Protocols/Selectable.swift b/TableViewKit/Protocols/Selectable.swift index b268edb..b37abad 100644 --- a/TableViewKit/Protocols/Selectable.swift +++ b/TableViewKit/Protocols/Selectable.swift @@ -11,22 +11,22 @@ import Foundation public protocol Selectable: Item { var onSelection: (Selectable) -> () { get set } - func selectRow(inManager manager: TableViewManager, animated: Bool, scrollPosition: UITableViewScrollPosition) - func deselectRow(inManager manager: TableViewManager, animated: Bool) + func select(in manager: TableViewManager, animated: Bool, scrollPosition: UITableViewScrollPosition) + func deselect(in manager: TableViewManager, animated: Bool) } extension Selectable { - public func selectRow(inManager manager: TableViewManager, animated: Bool, scrollPosition: UITableViewScrollPosition = .None) { + public func select(in manager: TableViewManager, animated: Bool, scrollPosition: UITableViewScrollPosition = .none) { - manager.tableView.selectRowAtIndexPath(indexPath(inManager: manager), animated: animated, scrollPosition: scrollPosition) + manager.tableView.selectRow(at: indexPath(in: manager), animated: animated, scrollPosition: scrollPosition) + manager.tableView(manager.tableView, didSelectRowAt: indexPath(in: manager)!) } - public func deselectRow(inManager manager: TableViewManager, animated: Bool) { - - if let itemIndexPath = indexPath(inManager: manager) { - manager.tableView.deselectRowAtIndexPath(itemIndexPath, animated: animated) - } + public func deselect(in manager: TableViewManager, animated: Bool) { + guard let indexPath = indexPath(in: manager) else { return } + + manager.tableView.deselectRow(at: indexPath, animated: animated) } } diff --git a/TableViewKit/Section.swift b/TableViewKit/Section.swift index 4ba67e5..984a50c 100644 --- a/TableViewKit/Section.swift +++ b/TableViewKit/Section.swift @@ -9,57 +9,73 @@ import Foundation import UIKit +/// A type that represent a section to be displayed +/// containing `items`, a `header` and a `footer` public protocol Section: class { + + /// A array containing the `items` of the section var items: ObservableArray { get set } + /// The `header` of the section, none if not defined + /// - Default: none var header: HeaderFooterView { get } + /// The `footer` of the section, none if not defined var footer: HeaderFooterView { get } - - func index(inManager manager: TableViewManager) -> Int? - func setup(inManager manager: TableViewManager) - func register(inManager manager: TableViewManager) } extension Section { public var header: HeaderFooterView { return nil } public var footer: HeaderFooterView { return nil } +} - public func index(inManager manager: TableViewManager) -> Int? { return manager.sections.indexOf(self) } - public func register(inManager manager: TableViewManager) { +extension Section { + + /// Returns the `index` of the `section` in the specified `manager` + /// + /// - parameter manager: A `manager` where the `section` may have been added + /// + /// - returns: The `index` of the `section` or `nil` if not present + public func index(in manager: TableViewManager) -> Int? { + return manager.sections.index(of: self) + } + + /// Register the section in the specified manager + /// + /// - parameter manager: A manager where the section may have been added + internal func register(in manager: TableViewManager) { if case .view(let header) = header { - manager.tableView.register(type: header.drawer.headerFooterType) + manager.tableView.register(header.drawer.type) } if case .view(let footer) = footer { - manager.tableView.register(type: footer.drawer.headerFooterType) + manager.tableView.register(footer.drawer.type) } items.forEach { - if let item = $0 as? Validationable { - manager.validator.add(validation: item.validation) - } - - manager.tableView.register(type: $0.drawer.cellType) + manager.tableView.register($0.drawer.type) } } - public func setup(inManager manager: TableViewManager) { + /// Setup the section internals + /// + /// - parameter manager: A manager where the section may have been added + internal func setup(in manager: TableViewManager) { items.callback = { change in - guard let sectionIndex = manager.sections.indexOf(self) else { return } + + guard let sectionIndex = manager.sections.index(of: self) else { return } let tableView = manager.tableView switch change { case .inserts(let array): - - let indexPaths = array.map { NSIndexPath(forRow: $0, inSection: sectionIndex) } - tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic) + let indexPaths = array.map { IndexPath(item: $0, section: sectionIndex) } + tableView.insertRows(at: indexPaths, with: .automatic) case .deletes(let array): - let indexPaths = array.map { NSIndexPath(forRow: $0, inSection: sectionIndex) } - tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic) + let indexPaths = array.map { IndexPath(item: $0, section: sectionIndex) } + tableView.deleteRows(at: indexPaths, with: .automatic) case .updates(let array): - let indexPaths = array.map { NSIndexPath(forRow: $0, inSection: sectionIndex) } - tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic) + let indexPaths = array.map { IndexPath(item: $0, section: sectionIndex) } + tableView.reloadRows(at: indexPaths, with: .automatic) case .moves(let array): - let fromIndexPaths = array.map { NSIndexPath(forRow: $0.0, inSection: sectionIndex) } - let toIndexPaths = array.map { NSIndexPath(forRow: $0.1, inSection: sectionIndex) } + let fromIndexPaths = array.map { IndexPath(item: $0.0, section: sectionIndex) } + let toIndexPaths = array.map { IndexPath(item: $0.1, section: sectionIndex) } tableView.moveRows(at: fromIndexPaths, to: toIndexPaths) case .beginUpdates: tableView.beginUpdates() @@ -71,8 +87,8 @@ extension Section { } } -extension CollectionType where Generator.Element == Section { - func indexOf(element: Generator.Element) -> Index? { - return indexOf({ $0 === element }) +public extension Collection where Iterator.Element == Section { + func index(of element: Iterator.Element) -> Index? { + return index(where: { $0 === element }) } } diff --git a/TableViewKit/TableViewManager.swift b/TableViewKit/TableViewManager.swift index dbada97..531374b 100644 --- a/TableViewKit/TableViewManager.swift +++ b/TableViewKit/TableViewManager.swift @@ -9,87 +9,84 @@ import Foundation import UIKit -public class TableViewManager: NSObject { - +open class TableViewManager: NSObject { + // MARK: Properties - public let tableView: UITableView - public var sections: ObservableArray
= [] - - public var validator: ValidatorManager = ValidatorManager() - public var errors: [ValidationError] { - get { - return validator.errors - } - } - - + open let tableView: UITableView + open var sections: ObservableArray
+ // MARK: Inits - + public init(tableView: UITableView) { self.tableView = tableView + self.sections = [] super.init() self.tableView.dataSource = self self.tableView.delegate = self + self.setupSections() + + } - sections.callback = { change in - + public convenience init(tableView: UITableView, with sections: [Section]) { + self.init(tableView: tableView) + self.sections.replace(with: sections) + } + + private func setupSections() { + sections.callback = { [weak self] change in + guard let weakSelf = self else { return } + switch change { case .inserts(let array): array.forEach { index in - self.sections[index].setup(inManager: self) - self.sections[index].register(inManager: self) + weakSelf.sections[index].setup(in: weakSelf) + weakSelf.sections[index].register(in: weakSelf) } - tableView.insertSections(NSIndexSet(array), withRowAnimation: .Automatic) + weakSelf.tableView.insertSections(IndexSet(array), with: .automatic) case .deletes(let array): - tableView.deleteSections(NSIndexSet(array), withRowAnimation: .Automatic) + weakSelf.tableView.deleteSections(IndexSet(array), with: .automatic) case .updates(let array): - tableView.reloadSections(NSIndexSet(array), withRowAnimation: .Automatic) + weakSelf.tableView.reloadSections(IndexSet(array), with: .automatic) case .moves(_): break case .beginUpdates: - tableView.beginUpdates() + weakSelf.tableView.beginUpdates() case .endUpdates: - tableView.endUpdates() + weakSelf.tableView.endUpdates() } - } } - - public convenience init(tableView: UITableView, sections: [Section]) { - self.init(tableView: tableView) - self.sections.insertContentsOf(sections, at: 0) - } } extension TableViewManager { - - private func item(forIndexPath indexPath: NSIndexPath) -> Item { + + fileprivate func item(at indexPath: IndexPath) -> Item { return sections[indexPath.section].items[indexPath.row] } - private func view(forKey key: (Section) -> HeaderFooterView, inSection section: Int) -> UIView? { + fileprivate func view(for key: (Section) -> HeaderFooterView, inSection section: Int) -> UIView? { guard case .view(let item) = key(sections[section]) else { return nil } let drawer = item.drawer - let view = drawer.view(inManager: self, withItem: item) - drawer.draw(view, withItem: item) + let view = drawer.view(in: self, with: item) + drawer.draw(view, with: item) return view } - private func title(forKey key: (Section) -> HeaderFooterView, inSection section: Int) -> String? { + fileprivate func title(for key: (Section) -> HeaderFooterView, inSection section: Int) -> String? { if case .title(let value) = key(sections[section]) { return value } return nil - + } - private func estimatedHeight(forKey key: (Section) -> HeaderFooterView, inSection section: Int) -> CGFloat? { + fileprivate func estimatedHeight(for key: (Section) -> HeaderFooterView, inSection section: Int) -> CGFloat? { let item = key(sections[section]) switch item { case .view(let view): guard let height = view.height else { return nil } - return estimatedHeight(height) + return height.estimated() case .title(_): return 1.0 default: @@ -97,115 +94,97 @@ extension TableViewManager { } } - private func estimatedHeight(atIndexPath indexPath: NSIndexPath) -> CGFloat? { - guard let height = item(forIndexPath: indexPath).height else { return nil } - return estimatedHeight(height) + fileprivate func estimatedHeight(at indexPath: IndexPath) -> CGFloat? { + guard let height = item(at: indexPath).height else { return nil } + return height.estimated() } - private func estimatedHeight(height: ImmutableMutableHeight) -> CGFloat { - switch height { - case .immutable(_): - return 0.0 - case .mutable(let value): - return value - } - } + - private func height(forKey key: (Section) -> HeaderFooterView, inSection section: Int) -> CGFloat? { + fileprivate func height(for key: (Section) -> HeaderFooterView, inSection section: Int) -> CGFloat? { guard case .view(let view) = key(sections[section]), let value = view.height else { return nil } - return height(value) + return value.height() } - private func height(atIndexPath indexPath: NSIndexPath) -> CGFloat? { - guard let value = item(forIndexPath: indexPath).height else { return nil } - return height(value) + fileprivate func height(at indexPath: IndexPath) -> CGFloat? { + guard let value = item(at: indexPath).height else { return nil } + return value.height() } - - private func height(height: ImmutableMutableHeight) -> CGFloat { - switch height { - case .immutable(let value): - return value - case .mutable(_): - return UITableViewAutomaticDimension - } - } - } extension TableViewManager: UITableViewDataSource { - - public func numberOfSectionsInTableView(tableView: UITableView) -> Int { + + public func numberOfSections(in tableView: UITableView) -> Int { return sections.count } - - public func tableView(tableView: UITableView, numberOfRowsInSection sectionIndex: Int) -> Int { + + public func tableView(_ tableView: UITableView, numberOfRowsInSection sectionIndex: Int) -> Int { let section = sections[sectionIndex] return section.items.count } - - public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { - - let currentItem = item(forIndexPath: indexPath) + + public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let currentItem = item(at: indexPath) let drawer = currentItem.drawer - - let cell = drawer.cell(inManager: self, withItem: currentItem, forIndexPath: indexPath) - drawer.draw(cell, withItem: currentItem) - + + let cell = drawer.cell(in: self, with: currentItem, for: indexPath) + drawer.draw(cell, with: currentItem) + return cell } - - - public func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - return title(forKey: {$0.header}, inSection: section) + + + public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return title(for: {$0.header}, inSection: section) } - - - public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? { - return title(forKey: {$0.footer}, inSection: section) + + + public func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { + return title(for: {$0.footer}, inSection: section) } - - + + } extension TableViewManager: UITableViewDelegate { - - public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { - guard let currentItem = item(forIndexPath: indexPath) as? Selectable else { return } + + public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let currentItem = item(at: indexPath) as? Selectable else { return } currentItem.onSelection(currentItem) } - - public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { - return height(atIndexPath: indexPath) ?? tableView.rowHeight + + public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return height(at: indexPath) ?? tableView.rowHeight } - - public func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - return height(forKey: {$0.header}, inSection: section) ?? tableView.sectionHeaderHeight + + public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return height(for: {$0.header}, inSection: section) ?? tableView.sectionHeaderHeight } - - public func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { - return height(forKey: {$0.footer}, inSection: section) ?? tableView.sectionFooterHeight + + public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + return height(for: {$0.footer}, inSection: section) ?? tableView.sectionFooterHeight } - - public func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { - return estimatedHeight(atIndexPath: indexPath) ?? tableView.estimatedRowHeight + + public func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + return estimatedHeight(at: indexPath) ?? tableView.estimatedRowHeight } - - public func tableView(tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat { - return estimatedHeight(forKey: {$0.header}, inSection: section) ?? tableView.estimatedSectionHeaderHeight + + public func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat { + return estimatedHeight(for: {$0.header}, inSection: section) ?? tableView.estimatedSectionHeaderHeight } - - public func tableView(tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat { - return estimatedHeight(forKey: {$0.footer}, inSection: section) ?? tableView.estimatedSectionHeaderHeight + + public func tableView(_ tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat { + return estimatedHeight(for: {$0.footer}, inSection: section) ?? tableView.estimatedSectionHeaderHeight } - - public func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - return view(forKey: {$0.header}, inSection: section) + + public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + return view(for: {$0.header}, inSection: section) } - - public func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { - return view(forKey: {$0.footer}, inSection: section) + + public func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + return view(for: {$0.footer}, inSection: section) } } diff --git a/TableViewKitTests/ActionBarTests.swift b/TableViewKitTests/ActionBarTests.swift deleted file mode 100644 index 4474322..0000000 --- a/TableViewKitTests/ActionBarTests.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// ActionBarTests.swift -// TableViewKit -// -// Created by Alfredo Delli Bovi on 28/08/16. -// Copyright © 2016 odigeo. All rights reserved. -// - -import XCTest - - -import XCTest -import TableViewKit -import Nimble - -// Maybe ActionBar and ActionBarTests should not be party of the library - -class ActionBarTests: XCTestCase { - - private var tableViewManager: TableViewManager! - - override func setUp() { - super.setUp() - let controller = UITableViewController(style: .Plain) - tableViewManager = TableViewManager(tableView: controller.tableView) - -// tableViewManager.sections.append( -// TestSection(items: [TextFieldItem(placeHolder: "0.0"), TextFieldItem(placeHolder: "0.1")]) -// ) -// tableViewManager.sections.append( -// TestSection(items: [TextFieldItem(placeHolder: "1.0")]) -// ) -// tableViewManager.tableView.reloadData() - } - - override func tearDown() { - tableViewManager = nil - super.tearDown() - } - - func testActionBar() { -// var cell: BaseCell -// cell = tableViewManager.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0)) as! BaseCell -// -// expect(cell.actionBar(cell.actionBar, direction: .next)).to(equal(NSIndexPath(forRow: 1, inSection: 0))) -// -// tableViewManager.sections.collection.first!.items.removeAtIndex(0) -// -// cell = tableViewManager.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0)) as! BaseCell -// -// expect(cell.actionBar(cell.actionBar, direction: .next)).to(equal(NSIndexPath(forRow: 0, inSection: 1))) -// expect(cell.actionBar(cell.actionBar, direction: .previous)).to(beNil()) -// -// cell = tableViewManager.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 1)) as! BaseCell -// expect(cell.actionBar(cell.actionBar, direction: .previous)).to(equal(NSIndexPath(forRow: 0, inSection: 0))) - } - - -} diff --git a/TableViewKitTests/TableViewDataSourceTests.swift b/TableViewKitTests/TableViewDataSourceTests.swift index 97adae6..b911e0b 100644 --- a/TableViewKitTests/TableViewDataSourceTests.swift +++ b/TableViewKitTests/TableViewDataSourceTests.swift @@ -10,7 +10,22 @@ import XCTest import TableViewKit import Nimble -class TestSection: Section { +extension HeaderFooterView: Equatable { + public static func == (lhs: HeaderFooterView, rhs: HeaderFooterView) -> Bool { + switch (lhs, rhs) { + case (.none, .none): + return true + case (.view(let lhs), .view(let rhs)): + return lhs === rhs + case (.title(let lhs), .title(let rhs)): + return lhs == rhs + default: + return false + } + } +} + +class HeaderFooterTitleSection: Section { var items: ObservableArray = [] weak var tableViewManager: TableViewManager! @@ -19,71 +34,76 @@ class TestSection: Section { convenience init(items: [Item]) { self.init() - self.items.insertContentsOf(items, at: 0) + self.items.insert(contentsOf: items, at: 0) } } class TestDrawer: CellDrawer { - static internal var cellType = CellType.Class(BaseCell.self) - - static internal func draw(cell: BaseCell, withItem item: Any) { } + static internal var type = CellType.class(TestCell.self) + static internal func draw(_ cell: UITableViewCell, with item: Any) { } } class TestItem: Item { internal var drawer: CellDrawer.Type = TestDrawer.self } +class TestCell: UITableViewCell, ItemCompatible { + internal var item: Item? +} + class TableViewDataSourceTests: XCTestCase { - private var tableViewManager: TableViewManager! - private var item: Item! - private var section: Section! + fileprivate var tableViewManager: TableViewManager! override func setUp() { super.setUp() - tableViewManager = TableViewManager(tableView: UITableView()) - item = TestItem() - section = TestSection() - section.items.append(item) + let section1 = HeaderFooterTitleSection(items: [TestItem()]) + let section2 = ViewHeaderFooterSection(items: [NoHeigthItem(), StaticHeigthItem()]) - tableViewManager.sections.append(section) - + tableViewManager = TableViewManager(tableView: UITableView(), with: [section1, section2]) } - override func tearDown() { tableViewManager = nil - item = nil super.tearDown() } func testCellForRow() { - let indexPath = NSIndexPath(forRow: 0, inSection: 0) - let cell = self.tableViewManager.tableView(self.tableViewManager.tableView, cellForRowAtIndexPath: indexPath) + let indexPath = IndexPath(row: 0, section: 0) + let cell = self.tableViewManager.tableView(self.tableViewManager.tableView, cellForRowAt: indexPath) - expect(cell).to(beAnInstanceOf(BaseCell)) + expect(cell).to(beAnInstanceOf(TestCell.self)) } func testNumberOfSections() { - let count = self.tableViewManager.numberOfSectionsInTableView(self.tableViewManager.tableView) - expect(count).to(be(1)) + let count = self.tableViewManager.numberOfSections(in: self.tableViewManager.tableView) + expect(count).to(equal(2)) } func testNumberOfRowsInSection() { let count = self.tableViewManager.tableView(self.tableViewManager.tableView, numberOfRowsInSection: 0) - expect(count).to(be(1)) + expect(count) == 1 } func testTitleForHeaderInSection() { let title = self.tableViewManager.tableView(self.tableViewManager.tableView, titleForHeaderInSection: 0)! + let section = tableViewManager.sections.first! + expect(HeaderFooterView.title(title)).to(equal(section.header)) } func testTitleForFooterInSection() { - let title = self.tableViewManager.tableView(self.tableViewManager.tableView, titleForFooterInSection: 0)! - expect(HeaderFooterView.title(title)).to(equal(section.footer)) + var title: String? + + title = self.tableViewManager.tableView(self.tableViewManager.tableView, titleForFooterInSection: 0) + + let section = tableViewManager.sections.first! + expect(HeaderFooterView.title(title!)).to(equal(section.footer)) + + title = self.tableViewManager.tableView(self.tableViewManager.tableView, titleForFooterInSection: 1) + expect(title).to(beNil()) } func testViewForHeaderInSection() { @@ -92,21 +112,11 @@ class TableViewDataSourceTests: XCTestCase { } func testViewForFooterInSection() { - let view = self.tableViewManager.tableView(self.tableViewManager.tableView, viewForFooterInSection: 0) + var view: UIView? + view = self.tableViewManager.tableView(self.tableViewManager.tableView, viewForFooterInSection: 0) expect(view).to(beNil()) - } - func testEstimatedHeightForRowAtIndexPath() { - let indexPath = NSIndexPath(forRow: 0, inSection: 0) - let height = self.tableViewManager.tableView(self.tableViewManager.tableView, estimatedHeightForRowAtIndexPath: indexPath) - expect(height).to(equal(44.0)) + view = self.tableViewManager.tableView(self.tableViewManager.tableView, viewForFooterInSection: 1) + expect(view).notTo(beNil()) } - - - func testHeightForRowAtIndexPath() { - let indexPath = NSIndexPath(forRow: 0, inSection: 0) - let height = self.tableViewManager.tableView(self.tableViewManager.tableView, heightForRowAtIndexPath: indexPath) - expect(height).to(equal(UITableViewAutomaticDimension)) - } - } diff --git a/TableViewKitTests/TableViewDelegateTests.swift b/TableViewKitTests/TableViewDelegateTests.swift new file mode 100644 index 0000000..413fc26 --- /dev/null +++ b/TableViewKitTests/TableViewDelegateTests.swift @@ -0,0 +1,217 @@ +// +// TableViewDelegateTests.swift +// TableViewKit +// +// Created by Alfredo Delli Bovi on 10/09/2016. +// Copyright © 2016 odigeo. All rights reserved. +// + +import XCTest +import TableViewKit +import Nimble + +class NoHeaderFooterSection: Section { + var items: ObservableArray = [] + + convenience init(items: [Item]) { + self.init() + self.items.insert(contentsOf: items, at: 0) + } +} + +class CustomHeaderFooterView: UITableViewHeaderFooterView { + var label: UILabel = UILabel() + + override init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + + +class CustomHeaderDrawer: HeaderFooterDrawer { + + static public var type = HeaderFooterType.class(CustomHeaderFooterView.self) + + static public func draw(_ view: UITableViewHeaderFooterView, with item: Any) { + let item = item as! ViewHeaderFooter + let view = view as! CustomHeaderFooterView + view.label.text = item.title + } +} + + +class ViewHeaderFooter: HeaderFooter { + + public var title: String? + public var height: Height? = .dynamic(44.0) + public var drawer: HeaderFooterDrawer.Type = CustomHeaderDrawer.self + + public init() { } + + public convenience init(title: String) { + self.init() + self.title = title + } +} + +class ViewHeaderFooterSection: Section { + var items: ObservableArray = [] + + internal var header: HeaderFooterView = .view(ViewHeaderFooter(title: "First Section")) + internal var footer: HeaderFooterView = .view(ViewHeaderFooter(title: "Section Footer\nHola")) + + convenience init(items: [Item]) { + self.init() + self.items.insert(contentsOf: items, at: 0) + } +} + +class NoHeigthItem: Item { + internal var drawer: CellDrawer.Type = TestDrawer.self + + internal var height: Height? = nil +} + +class StaticHeigthItem: Item { + static let testStaticHeightValue: CGFloat = 20.0 + + internal var drawer: CellDrawer.Type = TestDrawer.self + + internal var height: Height? = .static(20.0) +} + +class SelectableItem: Selectable, Item { + public var onSelection: (Selectable) -> () + + internal var drawer: CellDrawer.Type = TestDrawer.self + + public init(callback: @escaping (Selectable) -> ()) { + onSelection = callback + } +} + + +class TableViewDelegateTests: XCTestCase { + + private var tableViewManager: TableViewManager! + + override func setUp() { + super.setUp() + tableViewManager = TableViewManager(tableView: UITableView()) + + tableViewManager.sections.append(HeaderFooterTitleSection(items: [TestItem()])) + tableViewManager.sections.append(NoHeaderFooterSection(items: [NoHeigthItem(), StaticHeigthItem()])) + tableViewManager.sections.append(ViewHeaderFooterSection(items: [NoHeigthItem(), StaticHeigthItem()])) + + } + + override func tearDown() { + tableViewManager = nil + super.tearDown() + } + + func testEstimatedHeightForHeader() { + var height: CGFloat + + height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForHeaderInSection: 0) + expect(height).to(beGreaterThan(0.0)) + + height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForHeaderInSection: 1) + expect(height).to(equal(tableViewManager.tableView.estimatedSectionHeaderHeight)) + } + + + func testHeightForHeader() { + var height: CGFloat + + height = tableViewManager.tableView(tableViewManager.tableView, heightForHeaderInSection: 0) + expect(height).to(equal(UITableViewAutomaticDimension)) + } + + func testEstimatedHeightForFooter() { + var height: CGFloat + + height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForFooterInSection: 0) + expect(height).to(beGreaterThan(0.0)) + + height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForFooterInSection: 1) + expect(height).to(equal(tableViewManager.tableView.estimatedSectionFooterHeight)) + + height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForFooterInSection: 2) + expect(height).to(equal(44.0)) + } + + func testHeightForFooter() { + var height: CGFloat + + height = tableViewManager.tableView(tableViewManager.tableView, heightForFooterInSection: 0) + expect(height).to(equal(UITableViewAutomaticDimension)) + + height = tableViewManager.tableView(tableViewManager.tableView, heightForFooterInSection: 2) + expect(height).to(equal(UITableViewAutomaticDimension)) + } + + func testEstimatedHeightForRowAtIndexPath() { + var height: CGFloat + var indexPath: IndexPath + + indexPath = IndexPath(row: 0, section: 0) + height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForRowAt: indexPath) + expect(height).to(equal(44.0)) + + indexPath = IndexPath(row: 0, section: 1) + height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForRowAt: indexPath) + expect(height).to(equal(tableViewManager.tableView.estimatedRowHeight)) + + indexPath = IndexPath(row: 1, section: 1) + height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForRowAt: indexPath) + expect(height).to(equal(0.0)) + } + + func testHeightForRowAtIndexPath() { + var height: CGFloat + var indexPath: IndexPath + + indexPath = IndexPath(row: 0, section: 0) + height = tableViewManager.tableView(tableViewManager.tableView, heightForRowAt: indexPath) + expect(height).to(equal(UITableViewAutomaticDimension)) + + indexPath = IndexPath(row: 0, section: 1) + height = tableViewManager.tableView(tableViewManager.tableView, heightForRowAt: indexPath) + expect(height).to(equal(tableViewManager.tableView.rowHeight)) + + indexPath = IndexPath(row: 1, section: 1) + height = tableViewManager.tableView(tableViewManager.tableView, heightForRowAt: indexPath) + expect(height).to(equal(StaticHeigthItem.testStaticHeightValue)) + } + + func testSelectRow() { + var indexPath: IndexPath + + indexPath = IndexPath(row: 0, section: 0) + tableViewManager.tableView(tableViewManager.tableView, didSelectRowAt: indexPath) + + var check = 0; + + let section = tableViewManager.sections[0] + indexPath = IndexPath(row: section.items.count, section: 0) + let item = SelectableItem(callback: { _ in + check += 1 + }) + section.items.append(item) + + tableViewManager.tableView(tableViewManager.tableView, didSelectRowAt: indexPath) + expect(check).to(equal(1)) + + item.select(in: tableViewManager, animated: true) + expect(check).to(equal(2)) + + item.deselect(in: tableViewManager, animated: true) + expect(check).to(equal(2)) + } + +} diff --git a/TableViewKitTests/TableViewKitTests.swift b/TableViewKitTests/TableViewKitTests.swift index fec27e4..db2241e 100644 --- a/TableViewKitTests/TableViewKitTests.swift +++ b/TableViewKitTests/TableViewKitTests.swift @@ -12,43 +12,119 @@ import Nimble @testable import TableViewKit -class TableViewKitTests: XCTestCase { +class TestReloadDrawer: CellDrawer { + + static internal var type = CellType.class(UITableViewCell.self) + + static internal func draw(_ cell: UITableViewCell, with item: Any) { + cell.textLabel?.text = (item as! TestReloadItem).title + } +} - private var tableViewManager: TableViewManager! +class TestReloadItem: Item { + internal var drawer: CellDrawer.Type = TestReloadDrawer.self + internal var title: String? +} - override func setUp() { +class TestRegisterNibCell: UITableViewCell { } +class TestRegisterHeaderFooterView: UITableViewHeaderFooterView { } - super.setUp() +class TableViewKitTests: XCTestCase { - tableViewManager = TableViewManager(tableView: UITableView()) + override func setUp() { + super.setUp() } override func tearDown() { - - tableViewManager = nil - super.tearDown() } func testAddSection() { + let tableViewManager = TableViewManager(tableView: UITableView()) - let section = TestSection() + let section = HeaderFooterTitleSection() tableViewManager.sections.append(section) - expect(self.tableViewManager.sections.count).to(equal(1)) + expect(tableViewManager.sections.count).to(equal(1)) } func testAddItem() { + let tableViewManager = TableViewManager(tableView: UITableView()) + let item: Item = TestItem() - let section = TestSection() + let section = HeaderFooterTitleSection() section.items.append(item) - tableViewManager.sections.append(section) + tableViewManager.sections.insert(section, at: 0) expect(section.items.count).to(equal(1)) - expect(item.section(inManager: self.tableViewManager)).notTo(beNil()) + expect(item.section(in: tableViewManager)).notTo(beNil()) + + section.items.remove(at: 0) + section.items.append(item) + + section.items.replace(with: [TestItem(), item]) + } + + func testConvenienceInit() { + let tableViewManager = TableViewManager(tableView: UITableView(), with: [HeaderFooterTitleSection()]) + + expect(tableViewManager.sections.count).to(equal(1)) } + + func testUpdateRow() { + + let item = TestReloadItem() + item.title = "Before" + + let section = HeaderFooterTitleSection(items: [item]) + let tableViewManager = TableViewManager(tableView: UITableView(), with: [section]) + + guard let indexPath = item.indexPath(in: tableViewManager) else { return } + var cell = tableViewManager.tableView(tableViewManager.tableView, cellForRowAt: indexPath) + + expect(cell.textLabel?.text).to(equal(item.title)) + + item.title = "After" + cell = tableViewManager.tableView(tableViewManager.tableView, cellForRowAt: indexPath) + + expect(cell.textLabel?.text).to(equal(item.title)) + } + + func testNoCrashOnNonAddedItem() { + let tableViewManager = TableViewManager(tableView: UITableView(), with: [HeaderFooterTitleSection()]) + let item: Item = TestReloadItem() + item.reload(in: tableViewManager, with: .automatic) + + let section = item.section(in: tableViewManager) + expect(section).to(beNil()) + } + + func testRegisterNibCells() { + + let testBundle = Bundle(for: TableViewKitTests.self) + let cellType = CellType.nib(UINib(nibName: String(describing: TestRegisterNibCell.self), bundle: testBundle), TestRegisterNibCell.self) + + let tableView = UITableView() + tableView.register(cellType) + + let cell = tableView.dequeueReusableCell(withIdentifier: cellType.reusableIdentifier) + + expect(cell).toNot(equal(nil)) + } + + func testRegisterNibHeaderFooter() { + + let testBundle = Bundle(for: TableViewKitTests.self) + let headerFooterType = HeaderFooterType.nib(UINib(nibName: String(describing: TestRegisterHeaderFooterView.self), bundle: testBundle), TestRegisterHeaderFooterView.self) + + let tableView = UITableView() + tableView.register(headerFooterType) + + let headerFooterView = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerFooterType.reusableIdentifier) + expect(headerFooterView).toNot(equal(nil)) + } } diff --git a/TableViewKitTests/TestRegisterHeaderFooterView.xib b/TableViewKitTests/TestRegisterHeaderFooterView.xib new file mode 100644 index 0000000..1aa5036 --- /dev/null +++ b/TableViewKitTests/TestRegisterHeaderFooterView.xib @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/TableViewKitTests/TestRegisterNibCell.xib b/TableViewKitTests/TestRegisterNibCell.xib new file mode 100644 index 0000000..1b8d007 --- /dev/null +++ b/TableViewKitTests/TestRegisterNibCell.xib @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/TableViewKitTests/ValidationTests.swift b/TableViewKitTests/ValidationTests.swift deleted file mode 100644 index b47ad6b..0000000 --- a/TableViewKitTests/ValidationTests.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// ValidationTests.swift -// TableViewKit -// -// Created by Nelson Dominguez Leon on 30/06/16. -// Copyright © 2016 ODIGEO. All rights reserved. -// - -import XCTest -import TableViewKit -import Nimble - -class ValidationTests: XCTestCase { - - override func setUp() { - - super.setUp() - } - - override func tearDown() { - - super.tearDown() - } - - func testPresenceValidatorOK() { - - let presenceValidator = PresenceValidator() - let error = presenceValidator.validate("String", name: "Field Name", parameters: nil) - - expect(error).to(beNil()) - } - - func testPresenceValidatorKO() { - - let presenceValidator = PresenceValidator() - let error = presenceValidator.validate("", name: "Field Name", parameters: nil) - - expect(error).notTo(beNil()) - } - - func testEmailValidatorOK() { - - let emailValidator = EmailValidator() - let error = emailValidator.validate("good@email.com", name: "Email Field", parameters: nil) - - expect(error).to(beNil()) - } - - func testEmailValidatorKO() { - - let emailValidator = EmailValidator() - let error = emailValidator.validate("bad@email", name: "Email Field", parameters: nil) - - expect(error).notTo(beNil()) - } -} diff --git a/build.sh b/build.sh index b99c455..aa93fd2 100755 --- a/build.sh +++ b/build.sh @@ -4,6 +4,6 @@ set -o pipefail && time xcodebuild clean test \ -workspace TableViewKit.xcworkspace \ -scheme TableViewKit \ - -sdk iphonesimulator9.3 \ - -destination 'platform=iOS Simulator,name=iPhone 6s,OS=9.3' \ + -sdk iphonesimulator10.0 \ + -destination 'platform=iOS Simulator,name=iPhone 6s,OS=10.0' \ | xcpretty