Skip to content
This repository has been archived by the owner on Nov 30, 2022. It is now read-only.

Commit

Permalink
Merge pull request #7 from MerrickSapsford/feature/prioritisation
Browse files Browse the repository at this point in the history
Add prioritisation to listeners
  • Loading branch information
msaps authored Jan 21, 2017
2 parents 77fde7d + 231980e commit a149d8a
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Example/Listenable-Example/ListenerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ListenerViewController: UIViewController, ListenableObjectDelegate {
override func viewDidLoad() {
super.viewDidLoad()

self.listenableObject.add(listeners: [self, self.actionButton])
self.listenableObject.add(listeners: [self, self.actionButton], priority: .low)
}

// MARK: Actions
Expand Down
28 changes: 20 additions & 8 deletions Source/Listenable.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
objects = {

/* Begin PBXBuildFile section */
46F9E4311E318209005D7089 /* ListenerPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46F9E4301E318209005D7089 /* ListenerPriority.swift */; };
D6465F591E250337004E8623 /* ListenerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6465F581E250337004E8623 /* ListenerNode.swift */; };
D6465F5B1E250355004E8623 /* TestListenableObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6465F5A1E250355004E8623 /* TestListenableObject.swift */; };
D6465F5D1E25063F004E8623 /* TestListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6465F5C1E25063F004E8623 /* TestListener.swift */; };
D67206471E326E42003E22C3 /* TestListenableObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67206451E326E42003E22C3 /* TestListenableObject.swift */; };
D67206481E326E42003E22C3 /* TestListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = D67206461E326E42003E22C3 /* TestListener.swift */; };
D6F613BB1E24F59D004884E8 /* Listenable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6F613B11E24F59D004884E8 /* Listenable.framework */; };
D6F613C01E24F59D004884E8 /* ListenableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F613BF1E24F59D004884E8 /* ListenableTests.swift */; };
D6F613C21E24F59D004884E8 /* Listenable.h in Headers */ = {isa = PBXBuildFile; fileRef = D6F613B41E24F59D004884E8 /* Listenable.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand All @@ -27,9 +28,10 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
46F9E4301E318209005D7089 /* ListenerPriority.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListenerPriority.swift; sourceTree = "<group>"; };
D6465F581E250337004E8623 /* ListenerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListenerNode.swift; sourceTree = "<group>"; };
D6465F5A1E250355004E8623 /* TestListenableObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestListenableObject.swift; sourceTree = "<group>"; };
D6465F5C1E25063F004E8623 /* TestListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestListener.swift; sourceTree = "<group>"; };
D67206451E326E42003E22C3 /* TestListenableObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestListenableObject.swift; sourceTree = "<group>"; };
D67206461E326E42003E22C3 /* TestListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestListener.swift; sourceTree = "<group>"; };
D6F613B11E24F59D004884E8 /* Listenable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Listenable.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D6F613B41E24F59D004884E8 /* Listenable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Listenable.h; sourceTree = "<group>"; };
D6F613B51E24F59D004884E8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -58,6 +60,15 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
D67206441E326E42003E22C3 /* Objects */ = {
isa = PBXGroup;
children = (
D67206451E326E42003E22C3 /* TestListenableObject.swift */,
D67206461E326E42003E22C3 /* TestListener.swift */,
);
path = Objects;
sourceTree = "<group>";
};
D6F613A71E24F59D004884E8 = {
isa = PBXGroup;
children = (
Expand All @@ -81,6 +92,7 @@
children = (
D6F613B41E24F59D004884E8 /* Listenable.h */,
D6F613CB1E24F5F2004884E8 /* Listenable.swift */,
46F9E4301E318209005D7089 /* ListenerPriority.swift */,
D6465F581E250337004E8623 /* ListenerNode.swift */,
D6F613B51E24F59D004884E8 /* Info.plist */,
);
Expand All @@ -90,8 +102,7 @@
D6F613BE1E24F59D004884E8 /* ListenableTests */ = {
isa = PBXGroup;
children = (
D6465F5A1E250355004E8623 /* TestListenableObject.swift */,
D6465F5C1E25063F004E8623 /* TestListener.swift */,
D67206441E326E42003E22C3 /* Objects */,
D6F613BF1E24F59D004884E8 /* ListenableTests.swift */,
D6F613C11E24F59D004884E8 /* Info.plist */,
);
Expand Down Expand Up @@ -209,6 +220,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
46F9E4311E318209005D7089 /* ListenerPriority.swift in Sources */,
D6465F591E250337004E8623 /* ListenerNode.swift in Sources */,
D6F613CC1E24F5F2004884E8 /* Listenable.swift in Sources */,
);
Expand All @@ -219,8 +231,8 @@
buildActionMask = 2147483647;
files = (
D6F613C01E24F59D004884E8 /* ListenableTests.swift in Sources */,
D6465F5D1E25063F004E8623 /* TestListener.swift in Sources */,
D6465F5B1E250355004E8623 /* TestListenableObject.swift in Sources */,
D67206481E326E42003E22C3 /* TestListener.swift in Sources */,
D67206471E326E42003E22C3 /* TestListenableObject.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
39 changes: 35 additions & 4 deletions Source/Listenable/Listenable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,17 @@ open class Listenable<T>: AnyObject {
/// Add a new listener to the Listenable object.
///
/// - Parameter listener: The new listener to add.
/// - Paramter priority: Enumeration positional priority of the listener.
/// - Returns: Whether the listener was successfully added.
@discardableResult public func add(listener: T) -> Bool {
@discardableResult public func add(listener: T,
priority: ListenerPriority = .low) -> Bool {
if self.index(ofListener: listener) == nil {
self.listeners.append(ListenerNode(value: listener))

let insertionIndex = self.insertionIndex(forListenerWithPriority: priority)
let node = ListenerNode(value: listener,
priority: priority.value)
self.listeners.insert(node, at: insertionIndex)

return true
}
return false
Expand All @@ -49,9 +56,11 @@ open class Listenable<T>: AnyObject {
/// Add a number of new listeners to the Listenable object.
///
/// - Parameter listeners: The new listeners to add.
public func add(listeners: [T]) -> Void {
/// - Paramter priority: Enumeration positional priority of the listeners.
public func add(listeners: [T],
priority: ListenerPriority = .low) -> Void {
for listener in listeners {
self.add(listener: listener)
self.add(listener: listener, priority: priority)
}
}

Expand Down Expand Up @@ -109,4 +118,26 @@ open class Listenable<T>: AnyObject {
}
return index
}

private func insertionIndex(forListenerWithPriority priority: ListenerPriority) -> Int {
var insertionIndex: Int?

if priority != .low {

// enumerate until we find position where priority is less than desired
let priorityValue = priority.value
for (index, listener) in self.listeners.enumerated() {
if listener.priority < priorityValue {
insertionIndex = index
break
}
}
}

if let insertionIndex = insertionIndex {
return insertionIndex
}

return self.listeners.count
}
}
4 changes: 3 additions & 1 deletion Source/Listenable/ListenerNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import Foundation
internal class ListenerNode<T> {

weak var value: AnyObject?
var priority: Int

init(value: T) {
init(value: T, priority: Int) {
self.value = value as AnyObject
self.priority = priority
}
}
39 changes: 39 additions & 0 deletions Source/Listenable/ListenerPriority.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// ListenerPriority.swift
// Listenable
//
// Created by Merrick Sapsford on 19/01/2017.
// Copyright © 2017 Merrick Sapsford. All rights reserved.
//

import Foundation

/// Positional priority of a Listener within the enumeration queue.
/// Ranges from 1 (low) to 1000 (high).
///
/// - low: Listener is inserted at the end of the queue. (default)
/// - high: Listener is inserted at the front of the queue.
/// - custom: Custom queue prioritisation value (Range: 1 - 999)
public enum ListenerPriority: Equatable {

case low
case high
case custom(value: Int)

internal var value: Int {
get {
switch self {
case .high:
return 1000
case .custom(let value):
return value
default:
return 0
}
}
}

public static func == (lhs: ListenerPriority, rhs: ListenerPriority) -> Bool {
return lhs.value == rhs.value
}
}
65 changes: 64 additions & 1 deletion Source/ListenableTests/ListenableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class ListenableTests: XCTestCase {
self.listenableObject = TestListenableObject()
}

// MARK: Tests
// MARK: Add listeners

func testAddListener() {
let initialListenerCount = self.listenableObject.listenerCount
Expand All @@ -54,6 +54,67 @@ class ListenableTests: XCTestCase {
XCTAssert(successfulAdd == false, "Duplicate listener was able to be added")
}

// MARK: Prioritisation

func testAddHighPriorityListener() {
let lowPriorityListeners = [TestListener(), TestListener(), TestListener()]
self.listenableObject.add(listeners: lowPriorityListeners, priority: .low)

let highPriorityListener = TestListener()
self.listenableObject.add(listener: highPriorityListener, priority: .high)

var initialListener: TestListener!
self.listenableObject.updateListeners { (listener, index) in
if let listener = listener as? TestListener, index == 0 {
initialListener = listener
}
}

XCTAssert(initialListener === highPriorityListener,
"High priority listener was not correctly inserted at index 0")
}

func testAddLowPriorityListener() {
let highPriorityListeners = [TestListener(), TestListener(), TestListener()]
self.listenableObject.add(listeners: highPriorityListeners, priority: .high)

let lowPriorityListener = TestListener()
self.listenableObject.add(listener: lowPriorityListener, priority: .low)

var finalListener: TestListener!
self.listenableObject.updateListeners { (listener, index) in
if let listener = listener as? TestListener, index == self.listenableObject.listenerCount - 1 {
finalListener = listener
}
}

XCTAssert(finalListener === lowPriorityListener,
"Low priority listener was not correctly inserted at end of listener queue")
}

func testAddCustomPriorityListener() {
let highPriorityListeners = [TestListener()]
self.listenableObject.add(listeners: highPriorityListeners, priority: .high)

let lowPriorityListeners = [TestListener()]
self.listenableObject.add(listeners: lowPriorityListeners, priority: .low)

let customPriorityListener = TestListener()
self.listenableObject.add(listener: customPriorityListener, priority: .custom(value: 500))

var middleListener: TestListener!
self.listenableObject.updateListeners { (listener, index) in
if let listener = listener as? TestListener, index == self.listenableObject.listenerCount - lowPriorityListeners.count - 1 {
middleListener = listener
}
}

XCTAssert(middleListener === customPriorityListener,
"Custom priority (500) listener was not correctly inserted to the middle of the listener queue")
}

// MARK: Remove listeners

func testRemoveListener() {
let listeners = self.addTestListeners(count: 1,
toListenableObject: self.listenableObject)
Expand Down Expand Up @@ -102,6 +163,8 @@ class ListenableTests: XCTestCase {
"All listeners were not removed successfully")
}

// MARK: Enumerate listeners

func testEnumerateAllListeners() {
let proposedListenerCount = Int(arc4random_uniform(maxListenerCount) + 1)
self.addTestListeners(count: proposedListenerCount,
Expand Down

0 comments on commit a149d8a

Please sign in to comment.