diff --git a/LeadKit/LeadKit.xcodeproj/project.pbxproj b/LeadKit/LeadKit.xcodeproj/project.pbxproj index 57bfbfa4..f2aa7880 100644 --- a/LeadKit/LeadKit.xcodeproj/project.pbxproj +++ b/LeadKit/LeadKit.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 7830C4151C6B337D00180D02 /* CocoaLumberjack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7830C4141C6B337D00180D02 /* CocoaLumberjack.framework */; }; 78A74EA91C6B373700FE9724 /* UIView+DefaultNibName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78A74EA81C6B373700FE9724 /* UIView+DefaultNibName.swift */; }; - 78A74EAD1C6B408C00FE9724 /* ViewModelBuilderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78A74EAC1C6B408C00FE9724 /* ViewModelBuilderProtocol.swift */; }; 78B0FC7D1C6B2BE200358B64 /* LogFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B0FC7C1C6B2BE200358B64 /* LogFormatter.swift */; }; 78B0FC7F1C6B2C4D00358B64 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B0FC7E1C6B2C4D00358B64 /* Log.swift */; }; 78B0FC811C6B2CD500358B64 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B0FC801C6B2CD500358B64 /* App.swift */; }; @@ -27,6 +26,9 @@ 78CFEE591C5C45E500F50370 /* StoryboardIdentifierProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CFEE4E1C5C45E500F50370 /* StoryboardIdentifierProtocol.swift */; }; 78CFEE5A1C5C45E500F50370 /* ViewHeightProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CFEE4F1C5C45E500F50370 /* ViewHeightProtocol.swift */; }; 78CFEE5B1C5C45E500F50370 /* ViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CFEE501C5C45E500F50370 /* ViewModelProtocol.swift */; }; + 78E59B191C773EE600C6BFE9 /* ObjectsGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E59B181C773EE600C6BFE9 /* ObjectsGenerator.swift */; }; + 78E59B1B1C77470A00C6BFE9 /* TableViewCellsGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E59B1A1C77470A00C6BFE9 /* TableViewCellsGenerator.swift */; }; + 78E59B2A1C7861AF00C6BFE9 /* TableController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E59B291C7861AF00C6BFE9 /* TableController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -41,8 +43,7 @@ /* Begin PBXFileReference section */ 7830C4141C6B337D00180D02 /* CocoaLumberjack.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CocoaLumberjack.framework; path = Carthage/Build/iOS/CocoaLumberjack.framework; sourceTree = SOURCE_ROOT; }; - 78A74EA81C6B373700FE9724 /* UIView+DefaultNibName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+DefaultNibName.swift"; sourceTree = ""; }; - 78A74EAC1C6B408C00FE9724 /* ViewModelBuilderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModelBuilderProtocol.swift; sourceTree = ""; }; + 78A74EA81C6B373700FE9724 /* UIView+DefaultNibName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+DefaultNibName.swift"; path = "LeadKit/Extensions/UIView/UIView+DefaultNibName.swift"; sourceTree = SOURCE_ROOT; }; 78B0FC7C1C6B2BE200358B64 /* LogFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogFormatter.swift; sourceTree = ""; }; 78B0FC7E1C6B2C4D00358B64 /* Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; 78B0FC801C6B2CD500358B64 /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; @@ -53,9 +54,9 @@ 78CFEE391C5C456B00F50370 /* LeadKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeadKitTests.swift; sourceTree = ""; }; 78CFEE3B1C5C456B00F50370 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 78CFEE451C5C45E500F50370 /* UIStoryboard+InstantiateViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard+InstantiateViewController.swift"; sourceTree = ""; }; - 78CFEE461C5C45E500F50370 /* UITableView+CellRegistration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITableView+CellRegistration.swift"; sourceTree = ""; }; - 78CFEE471C5C45E500F50370 /* UITableView+DequeueCustomCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITableView+DequeueCustomCell.swift"; sourceTree = ""; }; - 78CFEE481C5C45E500F50370 /* UIView+LoadFromNib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+LoadFromNib.swift"; sourceTree = ""; }; + 78CFEE461C5C45E500F50370 /* UITableView+CellRegistration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UITableView+CellRegistration.swift"; path = "LeadKit/Extensions/UITableView/UITableView+CellRegistration.swift"; sourceTree = SOURCE_ROOT; }; + 78CFEE471C5C45E500F50370 /* UITableView+DequeueCustomCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UITableView+DequeueCustomCell.swift"; path = "LeadKit/Extensions/UITableView/UITableView+DequeueCustomCell.swift"; sourceTree = SOURCE_ROOT; }; + 78CFEE481C5C45E500F50370 /* UIView+LoadFromNib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+LoadFromNib.swift"; path = "LeadKit/Extensions/UIView/UIView+LoadFromNib.swift"; sourceTree = SOURCE_ROOT; }; 78CFEE4A1C5C45E500F50370 /* NibNameProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibNameProtocol.swift; sourceTree = ""; }; 78CFEE4B1C5C45E500F50370 /* ReuseIdentifierProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReuseIdentifierProtocol.swift; sourceTree = ""; }; 78CFEE4C1C5C45E500F50370 /* StaticNibNameProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StaticNibNameProtocol.swift; sourceTree = ""; }; @@ -63,6 +64,9 @@ 78CFEE4E1C5C45E500F50370 /* StoryboardIdentifierProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardIdentifierProtocol.swift; sourceTree = ""; }; 78CFEE4F1C5C45E500F50370 /* ViewHeightProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewHeightProtocol.swift; sourceTree = ""; }; 78CFEE501C5C45E500F50370 /* ViewModelProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModelProtocol.swift; sourceTree = ""; }; + 78E59B181C773EE600C6BFE9 /* ObjectsGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectsGenerator.swift; sourceTree = ""; }; + 78E59B1A1C77470A00C6BFE9 /* TableViewCellsGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewCellsGenerator.swift; sourceTree = ""; }; + 78E59B291C7861AF00C6BFE9 /* TableController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -96,6 +100,8 @@ 78A74EAA1C6B401800FE9724 /* Classes */ = { isa = PBXGroup; children = ( + 78E59B281C78613B00C6BFE9 /* TableView */, + 78E59B171C773E9600C6BFE9 /* Cache */, 78B0FC7B1C6B2BAE00358B64 /* Logging */, ); path = Classes; @@ -154,11 +160,9 @@ 78CFEE441C5C45E500F50370 /* Extensions */ = { isa = PBXGroup; children = ( + 78E59B2C1C786CD500C6BFE9 /* UIView */, + 78E59B2B1C786CBF00C6BFE9 /* UITableView */, 78CFEE451C5C45E500F50370 /* UIStoryboard+InstantiateViewController.swift */, - 78CFEE461C5C45E500F50370 /* UITableView+CellRegistration.swift */, - 78CFEE471C5C45E500F50370 /* UITableView+DequeueCustomCell.swift */, - 78CFEE481C5C45E500F50370 /* UIView+LoadFromNib.swift */, - 78A74EA81C6B373700FE9724 /* UIView+DefaultNibName.swift */, ); path = Extensions; sourceTree = ""; @@ -173,11 +177,45 @@ 78CFEE4D1C5C45E500F50370 /* StaticViewHeightProtocol.swift */, 78CFEE4F1C5C45E500F50370 /* ViewHeightProtocol.swift */, 78CFEE501C5C45E500F50370 /* ViewModelProtocol.swift */, - 78A74EAC1C6B408C00FE9724 /* ViewModelBuilderProtocol.swift */, ); path = Protocols; sourceTree = ""; }; + 78E59B171C773E9600C6BFE9 /* Cache */ = { + isa = PBXGroup; + children = ( + 78E59B181C773EE600C6BFE9 /* ObjectsGenerator.swift */, + 78E59B1A1C77470A00C6BFE9 /* TableViewCellsGenerator.swift */, + ); + path = Cache; + sourceTree = ""; + }; + 78E59B281C78613B00C6BFE9 /* TableView */ = { + isa = PBXGroup; + children = ( + 78E59B291C7861AF00C6BFE9 /* TableController.swift */, + ); + path = TableView; + sourceTree = ""; + }; + 78E59B2B1C786CBF00C6BFE9 /* UITableView */ = { + isa = PBXGroup; + children = ( + 78CFEE461C5C45E500F50370 /* UITableView+CellRegistration.swift */, + 78CFEE471C5C45E500F50370 /* UITableView+DequeueCustomCell.swift */, + ); + path = UITableView; + sourceTree = ""; + }; + 78E59B2C1C786CD500C6BFE9 /* UIView */ = { + isa = PBXGroup; + children = ( + 78CFEE481C5C45E500F50370 /* UIView+LoadFromNib.swift */, + 78A74EA81C6B373700FE9724 /* UIView+DefaultNibName.swift */, + ); + path = UIView; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -340,16 +378,18 @@ 78B0FC7F1C6B2C4D00358B64 /* Log.swift in Sources */, 78CFEE571C5C45E500F50370 /* StaticNibNameProtocol.swift in Sources */, 78CFEE531C5C45E500F50370 /* UITableView+DequeueCustomCell.swift in Sources */, + 78E59B1B1C77470A00C6BFE9 /* TableViewCellsGenerator.swift in Sources */, 78B0FC811C6B2CD500358B64 /* App.swift in Sources */, + 78E59B2A1C7861AF00C6BFE9 /* TableController.swift in Sources */, 78CFEE551C5C45E500F50370 /* NibNameProtocol.swift in Sources */, 78CFEE511C5C45E500F50370 /* UIStoryboard+InstantiateViewController.swift in Sources */, 78CFEE561C5C45E500F50370 /* ReuseIdentifierProtocol.swift in Sources */, + 78E59B191C773EE600C6BFE9 /* ObjectsGenerator.swift in Sources */, 78CFEE5B1C5C45E500F50370 /* ViewModelProtocol.swift in Sources */, 78CFEE5A1C5C45E500F50370 /* ViewHeightProtocol.swift in Sources */, 78A74EA91C6B373700FE9724 /* UIView+DefaultNibName.swift in Sources */, 78CFEE581C5C45E500F50370 /* StaticViewHeightProtocol.swift in Sources */, 78CFEE591C5C45E500F50370 /* StoryboardIdentifierProtocol.swift in Sources */, - 78A74EAD1C6B408C00FE9724 /* ViewModelBuilderProtocol.swift in Sources */, 78B0FC7D1C6B2BE200358B64 /* LogFormatter.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/LeadKit/LeadKit/Classes/Cache/ObjectsGenerator.swift b/LeadKit/LeadKit/Classes/Cache/ObjectsGenerator.swift new file mode 100644 index 00000000..376f5ef3 --- /dev/null +++ b/LeadKit/LeadKit/Classes/Cache/ObjectsGenerator.swift @@ -0,0 +1,59 @@ +// +// ObjectsPool.swift +// LeadKit +// +// Created by Иван Смолин on 19/02/16. +// Copyright © 2016 Touch Instinct. All rights reserved. +// + +import Foundation + +/// class that generates objects on initialization phase and then return its when necessary +public class ObjectsGenerator { + private var objects = [T]() + + private let poolSize: UInt + + typealias ObjectConstructor = () -> T + + private let objectsContructor: ObjectConstructor + + private let serialQueue = dispatch_queue_create("ru.touchin.LeadKit.ObjectsGenerator<\(T.self)>", DISPATCH_QUEUE_SERIAL) + + /** + initializer function + + - parameter poolSize: number of objects to generate + - parameter contructor: objects constructor closure + + - returns: nothing + */ + init(poolSize: UInt, objectsContructor contructor: ObjectConstructor) { + self.poolSize = poolSize + self.objectsContructor = contructor + + fillPool() + } + + private func fillPool() { + for _ in 0.. T { + dispatch_sync(serialQueue) { + if self.objects.count < 1 { + self.fillPool() + } + } + + return objects.popLast()! + } + +} diff --git a/LeadKit/LeadKit/Classes/Cache/TableViewCellsGenerator.swift b/LeadKit/LeadKit/Classes/Cache/TableViewCellsGenerator.swift new file mode 100644 index 00000000..0cb437f7 --- /dev/null +++ b/LeadKit/LeadKit/Classes/Cache/TableViewCellsGenerator.swift @@ -0,0 +1,26 @@ +// +// TableViewCellsPool.swift +// LeadKit +// +// Created by Иван Смолин on 19/02/16. +// Copyright © 2016 Touch Instinct. All rights reserved. +// + +import Foundation + +/// class that generates table view cells on initialization phase and then return its when necessary +public class TableViewCellsGenerator: ObjectsGenerator { + /** + initializer function + + - parameter poolSize: number of cells to generate + - parameter cellNibName: cell nib name + + - returns: nothing + */ + init(poolSize: UInt, cellNibName: String) { + super.init(poolSize: poolSize, objectsContructor: {() -> T in + T.loadFromNib(named: cellNibName) + }) + } +} diff --git a/LeadKit/LeadKit/Classes/TableView/TableController.swift b/LeadKit/LeadKit/Classes/TableView/TableController.swift new file mode 100644 index 00000000..61b64558 --- /dev/null +++ b/LeadKit/LeadKit/Classes/TableView/TableController.swift @@ -0,0 +1,63 @@ +// +// TableViewController.swift +// LeadKit +// +// Created by Иван Смолин on 20/02/16. +// Copyright © 2016 Touch Instinct. All rights reserved. +// + +import UIKit + +/// abstract class which holds few behaviour configuration properties, +/// inherit and add conformance for table view data source and delegate protocols +public class TableController: NSObject, UITableViewDataSource, UITableViewDelegate { + + /** + enumeration which describes how to calculate and store view models for cells + + - Precalculated: precacalculate view models for all cells and keep it in memory + - OnTheFlight: calculate view models when needed and forget it immideatelly + - OnTheFlightWithCache: calculate view models when needed and cache it + */ + internal enum TableControllerViewModelCalculationType { + case Precalculated + case OnTheFlight + case OnTheFlightWithCache + } + + /** + enumeration which describes how to create cells + + - Preloaded: load some amount of cells before data source delegate calls + - OnTheFlight: let table view deside when to load cells + */ + internal enum TableControllerCellCreationType { + case Preloaded + case OnTheFlight + } + + internal let viewModelCalculationType: TableControllerViewModelCalculationType + + internal let cellCreationType: TableControllerCellCreationType + + internal init(viewModelCalculationType calculationType: TableControllerViewModelCalculationType = .OnTheFlight, + cellCreationType creationType: TableControllerCellCreationType = .OnTheFlight) { + self.viewModelCalculationType = calculationType + self.cellCreationType = creationType + + super.init() + } + + public func numberOfSectionsInTableView(tableView: UITableView) -> Int { + preconditionFailure("subclass should implement \(__FUNCTION__)") + } + + public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + preconditionFailure("subclass should implement \(__FUNCTION__)") + } + + public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + preconditionFailure("subclass should implement \(__FUNCTION__)") + } + +} diff --git a/LeadKit/LeadKit/Extensions/UITableView+CellRegistration.swift b/LeadKit/LeadKit/Extensions/UITableView/UITableView+CellRegistration.swift similarity index 100% rename from LeadKit/LeadKit/Extensions/UITableView+CellRegistration.swift rename to LeadKit/LeadKit/Extensions/UITableView/UITableView+CellRegistration.swift diff --git a/LeadKit/LeadKit/Extensions/UITableView+DequeueCustomCell.swift b/LeadKit/LeadKit/Extensions/UITableView/UITableView+DequeueCustomCell.swift similarity index 100% rename from LeadKit/LeadKit/Extensions/UITableView+DequeueCustomCell.swift rename to LeadKit/LeadKit/Extensions/UITableView/UITableView+DequeueCustomCell.swift diff --git a/LeadKit/LeadKit/Extensions/UIView+DefaultNibName.swift b/LeadKit/LeadKit/Extensions/UIView/UIView+DefaultNibName.swift similarity index 87% rename from LeadKit/LeadKit/Extensions/UIView+DefaultNibName.swift rename to LeadKit/LeadKit/Extensions/UIView/UIView+DefaultNibName.swift index 215b61b1..25d38eff 100644 --- a/LeadKit/LeadKit/Extensions/UIView+DefaultNibName.swift +++ b/LeadKit/LeadKit/Extensions/UIView/UIView+DefaultNibName.swift @@ -6,7 +6,7 @@ // Copyright © 2016 Touch Instinct. All rights reserved. // -import Foundation +import UIKit extension UIView: StaticNibNameProtocol { /** @@ -15,7 +15,7 @@ extension UIView: StaticNibNameProtocol { - returns: class name string without dot (last class path component) */ - public static func nibName() -> String { + public class func nibName() -> String { return NSStringFromClass(self).componentsSeparatedByString(".").last! } diff --git a/LeadKit/LeadKit/Extensions/UIView+LoadFromNib.swift b/LeadKit/LeadKit/Extensions/UIView/UIView+LoadFromNib.swift similarity index 100% rename from LeadKit/LeadKit/Extensions/UIView+LoadFromNib.swift rename to LeadKit/LeadKit/Extensions/UIView/UIView+LoadFromNib.swift diff --git a/LeadKit/LeadKit/Protocols/ViewModelBuilderProtocol.swift b/LeadKit/LeadKit/Protocols/ViewModelBuilderProtocol.swift deleted file mode 100644 index 186fc7fa..00000000 --- a/LeadKit/LeadKit/Protocols/ViewModelBuilderProtocol.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// ViewModelBuilderProtocol.swift -// LeadKit -// -// Created by Иван Смолин on 10/02/16. -// Copyright © 2016 Touch Instinct. All rights reserved. -// - -import Foundation - -/** - * protocol which declares required methods for view model builder - */ -protocol ViewModelBuilderProtocol { - typealias ViewModelType - - /** - method which returns new view model - - - returns: view model object - */ - func buildViewModel() -> ViewModelType -}