Skip to content

Commit

Permalink
port to swift 3
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdrone committed Sep 12, 2016
1 parent 56b9719 commit a020b8d
Show file tree
Hide file tree
Showing 22 changed files with 507 additions and 279 deletions.
Binary file modified .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions Buffer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
TargetAttributes = {
160E255E1CF44C3700A5F828 = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 0800;
};
};
};
Expand Down Expand Up @@ -290,6 +291,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -307,6 +309,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.s.Buffer;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
};
name = Release;
};
Expand Down
6 changes: 3 additions & 3 deletions Buffer/AdapterType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ import Foundation

public protocol AdapterType {

associatedtype Type
associatedtype `Type`
associatedtype ViewType

///Returns the element currently on the front buffer at the given index path.
func displayedElementAtIndex(index: Int) -> Type
func displayedElement(at index: Int) -> Type

///The total number of elements currently displayed.
func countDisplayedElements() -> Int
Expand All @@ -42,7 +42,7 @@ public protocol AdapterType {
/// - parameter synchronous: Wether the filter, sorting and diff should be executed
/// synchronously or not.
/// - parameter completion: Code that will be executed once the buffer is updated.
func update(newValues: [Type]?, synchronous: Bool, completion: ((Void) -> Void)?)
func update(with values: [Type]?, synchronous: Bool, completion: ((Void) -> Void)?)

///The section index associated with this adapter.
var sectionIndex: Int { get set }
Expand Down
34 changes: 18 additions & 16 deletions Buffer/AnyListItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ public struct AnyListItem<Type: Equatable>: Equatable {
public var cellConfiguration: ((ListViewCell, Type) -> Void)?

#if os(iOS)
public init<V: PrototypeViewCell>(type: V.Type,
referenceView: ListContainerView,
reuseIdentifer: String = String(V.self),
state: Type,
configurationClosure: ((ListViewCell, Type) -> Void)? = nil) {
public init<V: PrototypeViewCell>(
type: V.Type,
referenceView: ListContainerView,
reuseIdentifer: String = String(describing: V.self),
state: Type,
configurationClosure: ((ListViewCell, Type) -> Void)? = nil) {

//registers the prototype cell if necessary.
if !Prototypes.isPrototypeCellRegistered(reuseIdentifer) {
Expand All @@ -70,33 +71,34 @@ public struct AnyListItem<Type: Equatable>: Equatable {
self.cellConfiguration = configurationClosure
self.referenceView = referenceView
self.state = state
self.configureRefenceView(type)
self.registerReferenceView(with: type)
}
#endif

public init<V: ListViewCell>(type: V.Type,
referenceView: ListContainerView? = nil,
reuseIdentifer: String = String(V.self),
state: Type,
configurationClosure: ((ListViewCell, Type) -> Void)? = nil) {
public init<V: ListViewCell>(
type: V.Type,
referenceView: ListContainerView? = nil,
reuseIdentifer: String = String(describing: V.self),
state: Type,
configurationClosure: ((ListViewCell, Type) -> Void)? = nil) {

self.reuseIdentifier = reuseIdentifer
self.cellConfiguration = configurationClosure
self.referenceView = referenceView
self.state = state
self.configureRefenceView(type)
self.registerReferenceView(with: type)
}

private func configureRefenceView(cellClass: AnyClass) {
fileprivate func registerReferenceView(with cellClass: AnyClass) {
#if os(iOS)
if let tableView = self.referenceView as? UITableView {
tableView.registerClass(cellClass,
tableView.register(cellClass,
forCellReuseIdentifier: self.reuseIdentifier)
}
if let collectionView = self.referenceView as? UICollectionView {
collectionView.registerClass(cellClass,
collectionView.register(cellClass,
forCellWithReuseIdentifier: self.reuseIdentifier)
}
#endif
}
}
}
123 changes: 63 additions & 60 deletions Buffer/Buffer.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

//
// Buffer.swift
// Buffer
Expand Down Expand Up @@ -32,27 +33,27 @@ public protocol BufferType { }
public protocol BufferDelegate: class {

/// Notifies the receiver that the content is about to change
func bufferWillChangeContent(buffer: BufferType)
func buffer(willChangeContent buffer: BufferType)

/// Notifies the receiver that rows were deleted.
func bufferDidDeleteElementAtIndices(buffer: BufferType, indices: [UInt])
func buffer(didDeleteElementAtIndices buffer: BufferType, indices: [UInt])

/// Notifies the receiver that rows were inserted.
func bufferDidInsertElementsAtIndices(buffer: BufferType, indices: [UInt])
func buffer(didInsertElementsAtIndices buffer: BufferType, indices: [UInt])

/// Notifies the receiver that the content updates has ended.
func bufferDidChangeContent(buffer: BufferType)
func buffer(didChangeContent buffer: BufferType)

/// Notifies the receiver that the content updates has ended.
/// This callback method is called when the number of changes are too many to be
/// handled for the UI thread - it's recommendable to just reload the whole data in this case.
/// - Note: The 'diffThreshold' property in 'Buffer' defines what is the maximum number
/// of changes
/// that you want the receiver to be notified for.
func bufferDidChangeAllContent(buffer: BufferType)
func buffer(didChangeAllContent buffer: BufferType)

/// Called when one of the observed properties for this object changed
func bufferDidChangeElementAtIndex(buffer: BufferType, index: UInt)
func buffer(didChangeElementAtIndex buffer: BufferType, index: UInt)
}

public class Buffer<ElementType: Equatable>: NSObject, BufferType {
Expand All @@ -69,39 +70,39 @@ public class Buffer<ElementType: Equatable>: NSObject, BufferType {
public var diffThreshold = 50

/// If set to 'true' the LCS algorithm is run synchronously on the main thread.
private var synchronous: Bool = false
fileprivate var synchronous: Bool = false

/// The two buffers.
private var frontBuffer = [ElementType]() {
fileprivate var frontBuffer = [ElementType]() {
willSet {
assert(NSThread.isMainThread())
self.observeTrackedKeyPaths(false)
assert(Thread.isMainThread)
self.observe(shouldObserveTrackedKeyPaths: false)
}
didSet {
assert(NSThread.isMainThread())
self.observeTrackedKeyPaths(true)
assert(Thread.isMainThread)
self.observe(shouldObserveTrackedKeyPaths: true)
}
}
private var backBuffer = [ElementType]()
fileprivate var backBuffer = [ElementType]()

/// Sort closure.
private var sort: ((ElementType, ElementType) -> Bool)?
fileprivate var sort: ((ElementType, ElementType) -> Bool)?

/// Filter closure.
private var filter: ((ElementType) -> Bool)?
fileprivate var filter: ((ElementType) -> Bool)?

/// The serial operation queue for this controller.
private let serialOperationQueue: NSOperationQueue = {
let operationQueue = NSOperationQueue()
fileprivate let serialOperationQueue: OperationQueue = {
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
return operationQueue
}()

private var flags = (isRefreshing: false,
fileprivate var flags = (isRefreshing: false,
shouldRefresh: false)

/// Used if 'Element' is KVO-compliant.
private var trackedKeyPaths = [String]()
fileprivate var trackedKeyPaths = [String]()

public init(initialArray: [ElementType],
sort: ((ElementType, ElementType) -> Bool)? = nil,
Expand All @@ -113,19 +114,19 @@ public class Buffer<ElementType: Equatable>: NSObject, BufferType {
}

deinit {
self.observeTrackedKeyPaths(false)
self.observe(shouldObserveTrackedKeyPaths: false)
}

/// Compute the diffs between the current array and the new one passed as argument.
public func update(
newValues: [ElementType]? = nil,
with values: [ElementType]? = nil,
synchronous: Bool = false,
completion: ((Void) -> Void)? = nil) {

let new = newValues ?? self.frontBuffer
let new = values ?? self.frontBuffer

// Should be called on the main thread.
assert(NSThread.isMainThread())
assert(Thread.isMainThread)

self.backBuffer = new

Expand All @@ -137,7 +138,7 @@ public class Buffer<ElementType: Equatable>: NSObject, BufferType {

self.flags.isRefreshing = true

self.dispatchOnSerialQueue(synchronous) {
self.dispatchOnSerialQueue(synchronous: synchronous) {

var backBuffer = self.backBuffer

Expand All @@ -146,34 +147,34 @@ public class Buffer<ElementType: Equatable>: NSObject, BufferType {
backBuffer = backBuffer.filter(filter)
}
if let sort = self.sort {
backBuffer = backBuffer.sort(sort)
backBuffer = backBuffer.sorted(by: sort)
}

// Compute the diff on the background thread.
let diff = self.frontBuffer.diff(backBuffer)

self.dispatchOnMainThread(synchronous) {
self.dispatchOnMainThread(synchronous: synchronous) {

//swaps the buffers.
self.frontBuffer = backBuffer

if diff.insertions.count < self.diffThreshold
&& diff.deletions.count < self.diffThreshold {
self.delegate?.bufferWillChangeContent(self)
self.delegate?.bufferDidInsertElementsAtIndices(
self, indices: diff.insertions.map({ UInt($0.idx) }))
self.delegate?.bufferDidDeleteElementAtIndices(
self, indices: diff.deletions.map({ UInt($0.idx) }))
self.delegate?.bufferDidChangeContent(self)
self.delegate?.buffer(willChangeContent: self)
self.delegate?.buffer(didInsertElementsAtIndices: self,
indices: diff.insertions.map({ UInt($0.idx) }))
self.delegate?.buffer(didDeleteElementAtIndices: self,
indices: diff.deletions.map({ UInt($0.idx) }))
self.delegate?.buffer(didChangeContent: self)
} else {
self.delegate?.bufferDidChangeAllContent(self)
self.delegate?.buffer(didChangeAllContent: self)
}

//re-rerun refresh if necessary.
self.flags.isRefreshing = false
if self.flags.shouldRefresh {
self.flags.shouldRefresh = false
self.update(self.backBuffer)
self.update(with: self.backBuffer)
}

completion?()
Expand All @@ -183,17 +184,17 @@ public class Buffer<ElementType: Equatable>: NSObject, BufferType {

/// This message is sent to the receiver when the value at the specified key path relative
/// to the given object has changed.
public override func observeValueForKeyPath(keyPath: String?,
ofObject object: AnyObject?,
change: [String : AnyObject]?,
context: UnsafeMutablePointer<Void>) {
public override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {

if context != &__observationContext {
super.observeValueForKeyPath(keyPath, ofObject:object, change:change, context:context)
super.observeValue(forKeyPath: keyPath, of:object, change:change, context:context)
return
}
if self.trackedKeyPaths.contains(keyPath!) {
self.objectDidChangeValueForKeyPath(keyPath, object: object)
self.objectDidChangeValue(for: keyPath, in: object as AnyObject?)
}

}
Expand All @@ -204,28 +205,28 @@ public class Buffer<ElementType: Equatable>: NSObject, BufferType {
extension Buffer where ElementType: AnyObject {

///Observe the keypaths passed as argument.
public func trackKeyPaths(keypaths: [String]) {
self.observeTrackedKeyPaths(false)
public func trackKeyPaths(_ keypaths: [String]) {
self.observe(shouldObserveTrackedKeyPaths: false)
self.trackedKeyPaths = keypaths
self.observeTrackedKeyPaths()
self.observe()
}
}

extension Buffer {

///Adds or remove observations.
///- Note: This code is executed only when 'Element: AnyObject'.
private func observeTrackedKeyPaths(observe: Bool = true) {
/// Adds or remove observations.
/// - Note: This code is executed only when 'Element: AnyObject'.
fileprivate func observe(shouldObserveTrackedKeyPaths: Bool = true) {
if self.trackedKeyPaths.count == 0 {
return
}
for keyPath in trackedKeyPaths {
for item in self.frontBuffer {
if let object = item as? AnyObject {
if observe {
if let object = item as? NSObject {
if shouldObserveTrackedKeyPaths {
object.addObserver(self,
forKeyPath: keyPath,
options: NSKeyValueObservingOptions.New,
options: NSKeyValueObservingOptions.new,
context: &__observationContext)

} else {
Expand All @@ -236,8 +237,8 @@ extension Buffer {
}
}

///- Note: This code is executed only when 'Element: AnyObject'.
private func objectDidChangeValueForKeyPath(keyPath: String?, object: AnyObject?) {
/// - Note: This code is executed only when 'Element: AnyObject'.
fileprivate func objectDidChangeValue(for keyPath: String?, in object: AnyObject?) {
dispatchOnMainThread {
self.update() {
var idx = 0
Expand All @@ -246,7 +247,7 @@ extension Buffer {
idx += 1
}
if idx < self.frontBuffer.count {
self.delegate?.bufferDidChangeElementAtIndex(self, index: UInt(idx))
self.delegate?.buffer(didChangeElementAtIndex: self, index: UInt(idx))
}
}
}
Expand All @@ -257,24 +258,26 @@ private var __observationContext: UInt8 = 0

//MARK: Dispatch Helpers

extension Buffer {
fileprivate extension Buffer {

private func dispatchOnMainThread(synchronous: Bool = false, block: (Void) -> (Void)) {
func dispatchOnMainThread(synchronous: Bool = false,
block: @escaping (Void) -> (Void)) {
if synchronous {
assert(NSThread.isMainThread())
assert(Thread.isMainThread)
block()
} else {
if NSThread.isMainThread() { block()
} else { dispatch_async(dispatch_get_main_queue(), block) }
if Thread.isMainThread { block()
} else { DispatchQueue.main.async(execute: block) }
}
}

private func dispatchOnSerialQueue(synchronous: Bool = false, block: (Void) -> (Void)) {
func dispatchOnSerialQueue(synchronous: Bool = false,
block: @escaping (Void) -> (Void)) {
if synchronous {
block()
} else {
self.serialOperationQueue.addOperationWithBlock(){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block)
self.serialOperationQueue.addOperation(){
DispatchQueue.global().async(execute: block)
}
}
}
Expand Down
Loading

0 comments on commit a020b8d

Please sign in to comment.