From b2a788392b8cfebc91d5d3348ac8d92ccb461ea2 Mon Sep 17 00:00:00 2001 From: Kasper Welner Date: Mon, 11 Jul 2016 19:21:46 +0200 Subject: [PATCH 01/10] Swift 3.0 implementations + predefined protocol and filter --- Noted.xcodeproj/project.pbxproj | 6 +++ Noted/Classes/Noted.swift | 66 ++++++++++++++++++++++++-------- NotedTests/NotedTests.swift | 2 +- NotedTests/ThreadSafeTests.swift | 24 ++++++++---- 4 files changed, 73 insertions(+), 25 deletions(-) diff --git a/Noted.xcodeproj/project.pbxproj b/Noted.xcodeproj/project.pbxproj index 9dc2594..d6668a8 100644 --- a/Noted.xcodeproj/project.pbxproj +++ b/Noted.xcodeproj/project.pbxproj @@ -170,9 +170,11 @@ TargetAttributes = { 272F1E111C6A4A250098F620 = { CreatedOnToolsVersion = 7.2.1; + LastSwiftMigration = 0800; }; 272F1E1B1C6A4A250098F620 = { CreatedOnToolsVersion = 7.2.1; + LastSwiftMigration = 0800; }; }; }; @@ -345,6 +347,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -363,6 +366,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.nodes.Noted; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -373,6 +377,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.nodes.NotedTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -383,6 +388,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.nodes.NotedTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/Noted/Classes/Noted.swift b/Noted/Classes/Noted.swift index b9cbf71..cc4dc97 100644 --- a/Noted/Classes/Noted.swift +++ b/Noted/Classes/Noted.swift @@ -7,38 +7,72 @@ import Foundation +public protocol NotificationObserver : AnyObject { + func didReceive(notification: Notification) +} + public protocol Notification { - func trigger(receiver: AnyObject) + func trigger(_ receiver: AnyObject) +} + +public protocol NotificationFilter { + func shouldFilter(notification:Notification) -> Bool +} + +internal struct PassthroughNotificationFilter : NotificationFilter { + internal func shouldFilter(notification:Notification) -> Bool { + return false + } +} + +private func ==(lhs: NotificationObserverInfo, rhs: NotificationObserverInfo) -> Bool { return lhs.receiver === rhs.receiver } + +private final class NotificationObserverInfo: AnyObject, Equatable { + + let receiver:NotificationObserver + let filter:NotificationFilter + + required init(receiver:NotificationObserver, filter: NotificationFilter) { + self.receiver = receiver + self.filter = filter + } } public class Noted { public static let defaultInstance = Noted() - private let notedQueue = dispatch_queue_create("com.noted.queue", DISPATCH_QUEUE_CONCURRENT) + private let notedQueue = DispatchQueue(label: "com.nodes.noted", attributes: .concurrent) - private init() {} + private var _observers = NSHashTable(options: .weakMemory) - var receivers = NSHashTable(options: NSPointerFunctionsOptions.WeakMemory) + public var receivers : [NotificationObserver] { + return _observers.allObjects.map {$0.receiver} + } + + private init() {} - public func addObserver(observer: AnyObject) { - dispatch_barrier_async(notedQueue) { [weak self] in - self?.receivers.addObject(observer) + public func addObserver(_ observer: NotificationObserver, filter: NotificationFilter = PassthroughNotificationFilter()) { + notedQueue.async(group: nil, qos: .default, flags: .barrier) { + self._observers.add(NotificationObserverInfo(receiver: observer, filter: filter)) } } - public func removeObserver(observer: AnyObject) { - dispatch_barrier_async(notedQueue) { [weak self] in - self?.receivers.removeObject(observer) + public func removeObserver(_ observer: NotificationObserver) { + notedQueue.async(group: nil, qos: .default, flags: .barrier) { + if let foundEntry = (self._observers.allObjects).first(where: {$0.receiver === observer}) { + self._observers.remove(foundEntry) + } } } - public func postNotification(notification: Notification) { - dispatch_async(notedQueue) { [weak self] in - for receiver in self?.receivers.allObjects ?? [] { - dispatch_async(dispatch_get_main_queue()) { [weak receiver] in - guard let receiver = receiver else { return } - notification.trigger(receiver) + public func postNotification(_ notification: Notification) { + notedQueue.async { + for receiver in self._observers.allObjects { + if !receiver.filter.shouldFilter(notification:notification) { + DispatchQueue.main.async { + notification.trigger(receiver) + } } } } diff --git a/NotedTests/NotedTests.swift b/NotedTests/NotedTests.swift index a71d90a..7c2cf3a 100644 --- a/NotedTests/NotedTests.swift +++ b/NotedTests/NotedTests.swift @@ -28,7 +28,7 @@ class NotedTests: XCTestCase { func testPerformanceExample() { // This is an example of a performance test case. - self.measureBlock { + self.measure { // Put the code you want to measure the time of here. } } diff --git a/NotedTests/ThreadSafeTests.swift b/NotedTests/ThreadSafeTests.swift index 79c9049..6dc7ed5 100644 --- a/NotedTests/ThreadSafeTests.swift +++ b/NotedTests/ThreadSafeTests.swift @@ -14,14 +14,20 @@ enum TestNotification { } extension TestNotification: Notification { - func trigger(reciever: AnyObject) { + func trigger(receiver: AnyObject) { // print(reciever) } } +class TestObserver: NotificationObserver { + func didReceive(notification: Notification) { + + } +} + class ThreadSafeTests: XCTestCase { - var recieverStore : [NSObject] = [] + var receiverStore : [TestObserver] = [] var notificationsCount : Int = 0 override func setUp() { @@ -31,20 +37,22 @@ class ThreadSafeTests: XCTestCase { override func tearDown() { super.tearDown() - self.recieverStore = [] + self.receiverStore = [] self.notificationsCount = 0 } func testThreadSafe() { for index in 0...300 { - let controller = NSObject() - recieverStore.append(recieverStore) - let queue = dispatch_queue_create("com.noted.queue.\(index)", nil) - dispatch_async(queue, { + let controller = TestObserver() + receiverStore.append(controller) + let queue = DispatchQueue(label:"com.noted.queue.\(index)") + queue.async { Noted.defaultInstance.addObserver(controller) Noted.defaultInstance.postNotification(TestNotification.TestUpdated) - }) + + } + } XCTAssert(true) } From 47da41cfc21e2b5781825204d851e4a6a655d85e Mon Sep 17 00:00:00 2001 From: Christian Graver Date: Thu, 1 Sep 2016 11:35:56 +0200 Subject: [PATCH 02/10] Updated deployment target to 9.0 --- Noted.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Noted.xcodeproj/project.pbxproj b/Noted.xcodeproj/project.pbxproj index d6668a8..26899f9 100644 --- a/Noted.xcodeproj/project.pbxproj +++ b/Noted.xcodeproj/project.pbxproj @@ -342,6 +342,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "$(SRCROOT)/Noted/Supporting Files/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.nodes.Noted; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -362,6 +363,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "$(SRCROOT)/Noted/Supporting Files/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.nodes.Noted; PRODUCT_NAME = "$(TARGET_NAME)"; From e2da198722acfbe4172cb2aea2c61b3bc02ac67c Mon Sep 17 00:00:00 2001 From: Jakob Mygind Date: Tue, 13 Sep 2016 13:00:46 +0200 Subject: [PATCH 03/10] Renamed Everything Notification to Note --- Noted.xcodeproj/project.pbxproj | 13 ++++-- .../xcshareddata/xcschemes/Noted.xcscheme | 2 +- Noted/Classes/Noted.swift | 43 ++++++++++--------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/Noted.xcodeproj/project.pbxproj b/Noted.xcodeproj/project.pbxproj index 26899f9..467d583 100644 --- a/Noted.xcodeproj/project.pbxproj +++ b/Noted.xcodeproj/project.pbxproj @@ -165,7 +165,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = Nodes; TargetAttributes = { 272F1E111C6A4A250098F620 = { @@ -255,8 +255,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"; @@ -279,7 +281,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -303,8 +305,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"; @@ -321,9 +325,10 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -336,6 +341,7 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -357,6 +363,7 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; diff --git a/Noted.xcodeproj/xcshareddata/xcschemes/Noted.xcscheme b/Noted.xcodeproj/xcshareddata/xcschemes/Noted.xcscheme index 4c40cc7..a64ff58 100644 --- a/Noted.xcodeproj/xcshareddata/xcschemes/Noted.xcscheme +++ b/Noted.xcodeproj/xcshareddata/xcschemes/Noted.xcscheme @@ -1,6 +1,6 @@ Bool +public protocol NoteFilter { + func shouldFilter(note: NoteType) -> Bool } -internal struct PassthroughNotificationFilter : NotificationFilter { - internal func shouldFilter(notification:Notification) -> Bool { +internal struct PassthroughNoteFilter: NoteFilter { + internal func shouldFilter(note: NoteType) -> Bool { return false } } -private func ==(lhs: NotificationObserverInfo, rhs: NotificationObserverInfo) -> Bool { return lhs.receiver === rhs.receiver } +private func ==(lhs: NoteObserverInfo, rhs: NoteObserverInfo) -> Bool { return lhs.receiver === rhs.receiver } -private final class NotificationObserverInfo: AnyObject, Equatable { +private final class NoteObserverInfo: AnyObject, Equatable { - let receiver:NotificationObserver - let filter:NotificationFilter + let receiver: NoteObserver + let filter:NoteFilter - required init(receiver:NotificationObserver, filter: NotificationFilter) { + required init(receiver: NoteObserver, filter: NoteFilter) { self.receiver = receiver self.filter = filter } @@ -42,23 +42,25 @@ public class Noted { public static let defaultInstance = Noted() + private let notedQueue = DispatchQueue(label: "com.nodes.noted", attributes: .concurrent) - private var _observers = NSHashTable(options: .weakMemory) + private var _observers = NSHashTable(options: .weakMemory) - public var receivers : [NotificationObserver] { + public var receivers : [NoteObserver] { return _observers.allObjects.map {$0.receiver} } + private init() {} - public func addObserver(_ observer: NotificationObserver, filter: NotificationFilter = PassthroughNotificationFilter()) { + public func addObserver(_ observer: NoteObserver, filter: NoteFilter = PassthroughNoteFilter()) { notedQueue.async(group: nil, qos: .default, flags: .barrier) { - self._observers.add(NotificationObserverInfo(receiver: observer, filter: filter)) + self._observers.add(NoteObserverInfo(receiver: observer, filter: filter)) } } - public func removeObserver(_ observer: NotificationObserver) { + public func removeObserver(_ observer: NoteObserver) { notedQueue.async(group: nil, qos: .default, flags: .barrier) { if let foundEntry = (self._observers.allObjects).first(where: {$0.receiver === observer}) { self._observers.remove(foundEntry) @@ -66,14 +68,15 @@ public class Noted { } } - public func postNotification(_ notification: Notification) { + public func postNote(_ note: NoteType) { notedQueue.async { for receiver in self._observers.allObjects { - if !receiver.filter.shouldFilter(notification:notification) { + if !receiver.filter.shouldFilter(note:note) { DispatchQueue.main.async { - notification.trigger(receiver) + note.trigger(receiver) } } + } } } From 13fbee65cb01ea1aeabc467dd7b2a4ba23bd9727 Mon Sep 17 00:00:00 2001 From: Jakob Mygind Date: Tue, 13 Sep 2016 14:03:59 +0200 Subject: [PATCH 04/10] Fixed tests for swift3 --- NotedTests/ThreadSafeTests.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/NotedTests/ThreadSafeTests.swift b/NotedTests/ThreadSafeTests.swift index 6dc7ed5..e64af99 100644 --- a/NotedTests/ThreadSafeTests.swift +++ b/NotedTests/ThreadSafeTests.swift @@ -13,14 +13,14 @@ enum TestNotification { case TestUpdated } -extension TestNotification: Notification { - func trigger(receiver: AnyObject) { +extension TestNotification: NoteType { + func trigger(_ receiver: AnyObject) { // print(reciever) } } -class TestObserver: NotificationObserver { - func didReceive(notification: Notification) { +class TestObserver: NoteObserver { + func didReceive(note: NoteType) { } } @@ -49,7 +49,7 @@ class ThreadSafeTests: XCTestCase { let queue = DispatchQueue(label:"com.noted.queue.\(index)") queue.async { Noted.defaultInstance.addObserver(controller) - Noted.defaultInstance.postNotification(TestNotification.TestUpdated) + Noted.defaultInstance.postNote(TestNotification.TestUpdated) } From d02bc481491ffd8b47b2c1f83f6e4a3b4c112ef8 Mon Sep 17 00:00:00 2001 From: Jakob Mygind Date: Tue, 13 Sep 2016 14:06:43 +0200 Subject: [PATCH 05/10] Made Noted more swift3y --- Noted/Classes/Noted.swift | 8 +++----- NotedTests/ThreadSafeTests.swift | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Noted/Classes/Noted.swift b/Noted/Classes/Noted.swift index ae4508b..a9b6230 100644 --- a/Noted/Classes/Noted.swift +++ b/Noted/Classes/Noted.swift @@ -42,7 +42,6 @@ public class Noted { public static let defaultInstance = Noted() - private let notedQueue = DispatchQueue(label: "com.nodes.noted", attributes: .concurrent) private var _observers = NSHashTable(options: .weakMemory) @@ -51,16 +50,15 @@ public class Noted { return _observers.allObjects.map {$0.receiver} } - private init() {} - public func addObserver(_ observer: NoteObserver, filter: NoteFilter = PassthroughNoteFilter()) { + public func add(observer: NoteObserver, filter: NoteFilter = PassthroughNoteFilter()) { notedQueue.async(group: nil, qos: .default, flags: .barrier) { self._observers.add(NoteObserverInfo(receiver: observer, filter: filter)) } } - public func removeObserver(_ observer: NoteObserver) { + public func remove(observer: NoteObserver) { notedQueue.async(group: nil, qos: .default, flags: .barrier) { if let foundEntry = (self._observers.allObjects).first(where: {$0.receiver === observer}) { self._observers.remove(foundEntry) @@ -68,7 +66,7 @@ public class Noted { } } - public func postNote(_ note: NoteType) { + public func post(note: NoteType) { notedQueue.async { for receiver in self._observers.allObjects { if !receiver.filter.shouldFilter(note:note) { diff --git a/NotedTests/ThreadSafeTests.swift b/NotedTests/ThreadSafeTests.swift index e64af99..00aedb1 100644 --- a/NotedTests/ThreadSafeTests.swift +++ b/NotedTests/ThreadSafeTests.swift @@ -48,8 +48,8 @@ class ThreadSafeTests: XCTestCase { receiverStore.append(controller) let queue = DispatchQueue(label:"com.noted.queue.\(index)") queue.async { - Noted.defaultInstance.addObserver(controller) - Noted.defaultInstance.postNote(TestNotification.TestUpdated) + Noted.defaultInstance.add(observer: controller) + Noted.defaultInstance.post(note: TestNotification.TestUpdated) } From 7d750379c979e6d48bd7f4833a5d6311ca9c1732 Mon Sep 17 00:00:00 2001 From: Dominik Hadl Date: Sat, 17 Sep 2016 16:25:27 +0200 Subject: [PATCH 06/10] Add all tests, fix issues with observers not being added --- Noted.xcodeproj/project.pbxproj | 34 ++++++- .../xcshareddata/xcschemes/Noted.xcscheme | 3 +- Noted/Classes/NoteFilter.swift | 19 ++++ Noted/Classes/NoteObserver.swift | 20 +++++ Noted/Classes/NoteType.swift | 13 +++ Noted/Classes/Noted.swift | 55 +++--------- NotedTests/NotedTests.swift | 90 +++++++++++++++---- NotedTests/Test Objects/TestNote.swift | 22 +++++ NotedTests/Test Objects/TestObserver.swift | 14 +++ NotedTests/ThreadSafeTests.swift | 59 ------------ 10 files changed, 204 insertions(+), 125 deletions(-) create mode 100644 Noted/Classes/NoteFilter.swift create mode 100644 Noted/Classes/NoteObserver.swift create mode 100644 Noted/Classes/NoteType.swift create mode 100644 NotedTests/Test Objects/TestNote.swift create mode 100644 NotedTests/Test Objects/TestObserver.swift delete mode 100644 NotedTests/ThreadSafeTests.swift diff --git a/Noted.xcodeproj/project.pbxproj b/Noted.xcodeproj/project.pbxproj index 467d583..8544970 100644 --- a/Noted.xcodeproj/project.pbxproj +++ b/Noted.xcodeproj/project.pbxproj @@ -9,9 +9,13 @@ /* Begin PBXBuildFile section */ 0135E62C1C7B5A6F007982E1 /* Noted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0135E6281C7B5A6F007982E1 /* Noted.swift */; }; 0135E62E1C7B5A6F007982E1 /* Noted.h in Headers */ = {isa = PBXBuildFile; fileRef = 0135E62B1C7B5A6F007982E1 /* Noted.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0159B06D1D8D862800467CC4 /* NoteObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0159B06C1D8D862800467CC4 /* NoteObserver.swift */; }; + 0159B06F1D8D863900467CC4 /* NoteType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0159B06E1D8D863900467CC4 /* NoteType.swift */; }; + 0159B0711D8D864300467CC4 /* NoteFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0159B0701D8D864300467CC4 /* NoteFilter.swift */; }; + 017E1C871D89AE0C00034114 /* TestNote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017E1C861D89AE0C00034114 /* TestNote.swift */; }; + 017E1C891D89AE1200034114 /* TestObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017E1C881D89AE1200034114 /* TestObserver.swift */; }; 272F1E1D1C6A4A250098F620 /* Noted.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 272F1E121C6A4A250098F620 /* Noted.framework */; }; 272F1E221C6A4A250098F620 /* NotedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272F1E211C6A4A250098F620 /* NotedTests.swift */; }; - E3586BF41D82EE6100859ED9 /* ThreadSafeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3586BF31D82EE6100859ED9 /* ThreadSafeTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -28,11 +32,15 @@ 0135E6281C7B5A6F007982E1 /* Noted.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Noted.swift; sourceTree = ""; }; 0135E62A1C7B5A6F007982E1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0135E62B1C7B5A6F007982E1 /* Noted.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Noted.h; sourceTree = ""; }; + 0159B06C1D8D862800467CC4 /* NoteObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoteObserver.swift; sourceTree = ""; }; + 0159B06E1D8D863900467CC4 /* NoteType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoteType.swift; sourceTree = ""; }; + 0159B0701D8D864300467CC4 /* NoteFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoteFilter.swift; sourceTree = ""; }; + 017E1C861D89AE0C00034114 /* TestNote.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNote.swift; sourceTree = ""; }; + 017E1C881D89AE1200034114 /* TestObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestObserver.swift; sourceTree = ""; }; 272F1E121C6A4A250098F620 /* Noted.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Noted.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 272F1E1C1C6A4A250098F620 /* NotedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NotedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 272F1E211C6A4A250098F620 /* NotedTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotedTests.swift; sourceTree = ""; }; 272F1E231C6A4A250098F620 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - E3586BF31D82EE6100859ED9 /* ThreadSafeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafeTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -58,6 +66,9 @@ isa = PBXGroup; children = ( 0135E6281C7B5A6F007982E1 /* Noted.swift */, + 0159B06C1D8D862800467CC4 /* NoteObserver.swift */, + 0159B06E1D8D863900467CC4 /* NoteType.swift */, + 0159B0701D8D864300467CC4 /* NoteFilter.swift */, ); path = Classes; sourceTree = ""; @@ -71,6 +82,15 @@ path = "Supporting Files"; sourceTree = ""; }; + 017E1C851D89AE0300034114 /* Test Objects */ = { + isa = PBXGroup; + children = ( + 017E1C861D89AE0C00034114 /* TestNote.swift */, + 017E1C881D89AE1200034114 /* TestObserver.swift */, + ); + path = "Test Objects"; + sourceTree = ""; + }; 272F1E081C6A4A250098F620 = { isa = PBXGroup; children = ( @@ -101,8 +121,8 @@ 272F1E201C6A4A250098F620 /* NotedTests */ = { isa = PBXGroup; children = ( + 017E1C851D89AE0300034114 /* Test Objects */, 272F1E211C6A4A250098F620 /* NotedTests.swift */, - E3586BF31D82EE6100859ED9 /* ThreadSafeTests.swift */, 272F1E231C6A4A250098F620 /* Info.plist */, ); path = NotedTests; @@ -218,7 +238,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0159B06F1D8D863900467CC4 /* NoteType.swift in Sources */, 0135E62C1C7B5A6F007982E1 /* Noted.swift in Sources */, + 0159B06D1D8D862800467CC4 /* NoteObserver.swift in Sources */, + 0159B0711D8D864300467CC4 /* NoteFilter.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -226,8 +249,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - E3586BF41D82EE6100859ED9 /* ThreadSafeTests.swift in Sources */, + 017E1C891D89AE1200034114 /* TestObserver.swift in Sources */, 272F1E221C6A4A250098F620 /* NotedTests.swift in Sources */, + 017E1C871D89AE0C00034114 /* TestNote.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -382,6 +406,7 @@ 272F1E2A1C6A4A250098F620 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_CODE_COVERAGE = NO; INFOPLIST_FILE = NotedTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.nodes.NotedTests; @@ -393,6 +418,7 @@ 272F1E2B1C6A4A250098F620 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_CODE_COVERAGE = NO; INFOPLIST_FILE = NotedTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.nodes.NotedTests; diff --git a/Noted.xcodeproj/xcshareddata/xcschemes/Noted.xcscheme b/Noted.xcodeproj/xcshareddata/xcschemes/Noted.xcscheme index a64ff58..b552a0f 100644 --- a/Noted.xcodeproj/xcshareddata/xcschemes/Noted.xcscheme +++ b/Noted.xcodeproj/xcshareddata/xcschemes/Noted.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/Noted/Classes/NoteFilter.swift b/Noted/Classes/NoteFilter.swift new file mode 100644 index 0000000..a467693 --- /dev/null +++ b/Noted/Classes/NoteFilter.swift @@ -0,0 +1,19 @@ +// +// NoteFilter.swift +// Noted +// +// Created by Dominik Hádl on 17/09/16. +// Copyright © 2016 Nodes. All rights reserved. +// + +import Foundation + +public protocol NoteFilter { + func shouldFilter(note: NoteType) -> Bool +} + +internal struct PassthroughNoteFilter: NoteFilter { + internal func shouldFilter(note: NoteType) -> Bool { + return false + } +} diff --git a/Noted/Classes/NoteObserver.swift b/Noted/Classes/NoteObserver.swift new file mode 100644 index 0000000..4faad3b --- /dev/null +++ b/Noted/Classes/NoteObserver.swift @@ -0,0 +1,20 @@ +// +// NoteObserver.swift +// Noted +// +// Created by Dominik Hádl on 17/09/16. +// Copyright © 2016 Nodes. All rights reserved. +// + +import Foundation + +public protocol NoteObserver: AnyObject { + func didReceive(note: NoteType) + var noteFilter: NoteFilter { get } +} + +extension NoteObserver { + public var noteFilter: NoteFilter { + return Noted.passthroughFilter + } +} diff --git a/Noted/Classes/NoteType.swift b/Noted/Classes/NoteType.swift new file mode 100644 index 0000000..89c1d22 --- /dev/null +++ b/Noted/Classes/NoteType.swift @@ -0,0 +1,13 @@ +// +// NoteType.swift +// Noted +// +// Created by Dominik Hádl on 17/09/16. +// Copyright © 2016 Nodes. All rights reserved. +// + +import Foundation + +public protocol NoteType { + func trigger(_ receiver: AnyObject) +} diff --git a/Noted/Classes/Noted.swift b/Noted/Classes/Noted.swift index a9b6230..895ff5c 100644 --- a/Noted/Classes/Noted.swift +++ b/Noted/Classes/Noted.swift @@ -7,60 +7,30 @@ import Foundation -public protocol NoteObserver: AnyObject { - func didReceive(note: NoteType) -} - -public protocol NoteType { - func trigger(_ receiver: AnyObject) -} - -public protocol NoteFilter { - func shouldFilter(note: NoteType) -> Bool -} - -internal struct PassthroughNoteFilter: NoteFilter { - internal func shouldFilter(note: NoteType) -> Bool { - return false - } -} - -private func ==(lhs: NoteObserverInfo, rhs: NoteObserverInfo) -> Bool { return lhs.receiver === rhs.receiver } - -private final class NoteObserverInfo: AnyObject, Equatable { - - let receiver: NoteObserver - let filter:NoteFilter - - required init(receiver: NoteObserver, filter: NoteFilter) { - self.receiver = receiver - self.filter = filter - } -} - public class Noted { public static let defaultInstance = Noted() private let notedQueue = DispatchQueue(label: "com.nodes.noted", attributes: .concurrent) - - private var _observers = NSHashTable(options: .weakMemory) - - public var receivers : [NoteObserver] { - return _observers.allObjects.map {$0.receiver} + internal var _observers = NSHashTable(options: .weakMemory) + + internal static let passthroughFilter = PassthroughNoteFilter() + + public var observers: [NoteObserver] { + return _observers.allObjects.flatMap({ $0 as? NoteObserver }) } private init() {} public func add(observer: NoteObserver, filter: NoteFilter = PassthroughNoteFilter()) { notedQueue.async(group: nil, qos: .default, flags: .barrier) { - self._observers.add(NoteObserverInfo(receiver: observer, filter: filter)) + self._observers.add(observer) } } public func remove(observer: NoteObserver) { notedQueue.async(group: nil, qos: .default, flags: .barrier) { - if let foundEntry = (self._observers.allObjects).first(where: {$0.receiver === observer}) { + if let foundEntry = (self._observers.allObjects).first(where: {$0 === observer}) { self._observers.remove(foundEntry) } } @@ -68,13 +38,10 @@ public class Noted { public func post(note: NoteType) { notedQueue.async { - for receiver in self._observers.allObjects { - if !receiver.filter.shouldFilter(note:note) { - DispatchQueue.main.async { - note.trigger(receiver) - } + for receiver in self.observers.filter({ !$0.noteFilter.shouldFilter(note: note) }) { + DispatchQueue.main.async { + note.trigger(receiver) } - } } } diff --git a/NotedTests/NotedTests.swift b/NotedTests/NotedTests.swift index 7c2cf3a..d0dd1b4 100644 --- a/NotedTests/NotedTests.swift +++ b/NotedTests/NotedTests.swift @@ -10,27 +10,83 @@ import XCTest @testable import Noted class NotedTests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - + + // Used for having strong reference to objects + var receiverStore: [TestObserver] = [] + override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. + Noted.defaultInstance._observers.removeAllObjects() + receiverStore = [] + TestNote.testTriggerAction = nil super.tearDown() } - - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. + + func testAddRemoveObserver() { + let observer = TestObserver() + receiverStore.append(observer) + + let expect = expectation(description: "Noted should finish adding and removing receiver.") + + XCTAssertEqual(Noted.defaultInstance.observers.count, 0, + "Noted shouldn't contain any receivers by default.") + + Noted.defaultInstance.add(observer: observer) + + DispatchQueue.main.asyncAfter(deadline: DispatchTime(secondsFromNow: 0.5)) { + XCTAssertEqual(Noted.defaultInstance.observers.count, 1, + "Noted should contain a receiver after adding it.") + + Noted.defaultInstance.remove(observer: observer) + + DispatchQueue.main.asyncAfter(deadline: DispatchTime(secondsFromNow: 0.5)) { + XCTAssertEqual(Noted.defaultInstance.observers.count, 0, + "Noted shouldn't contain a receiver after removing it.") + expect.fulfill() + } + } + + waitForExpectations(timeout: 10, handler: nil) + } + + func testPostNotification() { + let observer = TestObserver() + let expect = expectation(description: "A notification should be triggered.") + + TestNote.testTriggerAction = { receiver in + XCTAssert(observer === receiver, "The receiver of the notification should be the observer.") + expect.fulfill() + } + + receiverStore.append(observer) + + Noted.defaultInstance.add(observer: observer) + Noted.defaultInstance.post(note: TestNote.Test) + + waitForExpectations(timeout: 20, handler: nil) } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. + + func testThreadSafety() { + for index in 0...300 { + let controller = TestObserver() + receiverStore.append(controller) + + let queue = DispatchQueue(label: "com.noted.queue.\(index)") + + queue.async { + Noted.defaultInstance.add(observer: controller) + Noted.defaultInstance.post(note: TestNote.Test) + } } + + XCTAssert(true) + } +} + + +extension DispatchTime { + init(secondsFromNow seconds: Double) { + let now = DispatchTime.now().uptimeNanoseconds + let delay = UInt64(seconds * Double(NSEC_PER_SEC)) + self.init(uptimeNanoseconds: now + delay) } - } diff --git a/NotedTests/Test Objects/TestNote.swift b/NotedTests/Test Objects/TestNote.swift new file mode 100644 index 0000000..a46d207 --- /dev/null +++ b/NotedTests/Test Objects/TestNote.swift @@ -0,0 +1,22 @@ +// +// TestNote.swift +// Noted +// +// Created by Dominik Hádl on 14/09/16. +// Copyright © 2016 Nodes. All rights reserved. +// + +import Foundation +import Noted + +enum TestNote { + case Test +} + +extension TestNote: NoteType { + static var testTriggerAction: ((AnyObject) -> Void)? + + func trigger(_ receiver: AnyObject) { + TestNote.testTriggerAction?(receiver) + } +} diff --git a/NotedTests/Test Objects/TestObserver.swift b/NotedTests/Test Objects/TestObserver.swift new file mode 100644 index 0000000..1c0d151 --- /dev/null +++ b/NotedTests/Test Objects/TestObserver.swift @@ -0,0 +1,14 @@ +// +// TestObserver.swift +// Noted +// +// Created by Dominik Hádl on 14/09/16. +// Copyright © 2016 Nodes. All rights reserved. +// + +import Foundation +import Noted + +class TestObserver: NoteObserver { + func didReceive(note: NoteType) { } +} diff --git a/NotedTests/ThreadSafeTests.swift b/NotedTests/ThreadSafeTests.swift deleted file mode 100644 index 00aedb1..0000000 --- a/NotedTests/ThreadSafeTests.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// ThreadSafeTests.swift -// Noted -// -// Created by Todor Brachkov on 09/09/2016. -// Copyright © 2016 Nodes. All rights reserved. -// - -import XCTest -@testable import Noted - -enum TestNotification { - case TestUpdated -} - -extension TestNotification: NoteType { - func trigger(_ receiver: AnyObject) { -// print(reciever) - } -} - -class TestObserver: NoteObserver { - func didReceive(note: NoteType) { - - } -} - -class ThreadSafeTests: XCTestCase { - - var receiverStore : [TestObserver] = [] - var notificationsCount : Int = 0 - - override func setUp() { - super.setUp() - } - - override func tearDown() { - super.tearDown() - - self.receiverStore = [] - self.notificationsCount = 0 - } - - func testThreadSafe() { - - for index in 0...300 { - let controller = TestObserver() - receiverStore.append(controller) - let queue = DispatchQueue(label:"com.noted.queue.\(index)") - queue.async { - Noted.defaultInstance.add(observer: controller) - Noted.defaultInstance.post(note: TestNotification.TestUpdated) - - } - - } - XCTAssert(true) - } -} From 5d251220dd9d6f90c73641fb155843351d50c2c6 Mon Sep 17 00:00:00 2001 From: Dominik Hadl Date: Sat, 17 Sep 2016 16:53:56 +0200 Subject: [PATCH 07/10] Remove trigger func from NoteType, make accessing observers thread safe --- Noted/Classes/NoteType.swift | 8 +++++++- Noted/Classes/Noted.swift | 8 ++++++-- NotedTests/NotedTests.swift | 8 +++++--- NotedTests/Test Objects/TestNote.swift | 10 +++++----- NotedTests/Test Objects/TestObserver.swift | 6 +++++- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Noted/Classes/NoteType.swift b/Noted/Classes/NoteType.swift index 89c1d22..a81d4f1 100644 --- a/Noted/Classes/NoteType.swift +++ b/Noted/Classes/NoteType.swift @@ -9,5 +9,11 @@ import Foundation public protocol NoteType { - func trigger(_ receiver: AnyObject) + var context: AnyObject? { get } +} + +extension NoteType { + public var context: AnyObject? { + return nil + } } diff --git a/Noted/Classes/Noted.swift b/Noted/Classes/Noted.swift index 895ff5c..dd96203 100644 --- a/Noted/Classes/Noted.swift +++ b/Noted/Classes/Noted.swift @@ -17,7 +17,11 @@ public class Noted { internal static let passthroughFilter = PassthroughNoteFilter() public var observers: [NoteObserver] { - return _observers.allObjects.flatMap({ $0 as? NoteObserver }) + var values: [AnyObject] = [] + notedQueue.sync { + values = self._observers.allObjects + } + return values.flatMap({ $0 as? NoteObserver }) } private init() {} @@ -40,7 +44,7 @@ public class Noted { notedQueue.async { for receiver in self.observers.filter({ !$0.noteFilter.shouldFilter(note: note) }) { DispatchQueue.main.async { - note.trigger(receiver) + receiver.didReceive(note: note) } } } diff --git a/NotedTests/NotedTests.swift b/NotedTests/NotedTests.swift index d0dd1b4..564a516 100644 --- a/NotedTests/NotedTests.swift +++ b/NotedTests/NotedTests.swift @@ -17,7 +17,6 @@ class NotedTests: XCTestCase { override func tearDown() { Noted.defaultInstance._observers.removeAllObjects() receiverStore = [] - TestNote.testTriggerAction = nil super.tearDown() } @@ -52,8 +51,11 @@ class NotedTests: XCTestCase { let observer = TestObserver() let expect = expectation(description: "A notification should be triggered.") - TestNote.testTriggerAction = { receiver in - XCTAssert(observer === receiver, "The receiver of the notification should be the observer.") + observer.noteAction = { note in + let test = note as? TestNote + XCTAssert(test == TestNote.Test, + "The receiver of the notification should be the observer.") + XCTAssertNil(test?.context, "Default note context should be nil .") expect.fulfill() } diff --git a/NotedTests/Test Objects/TestNote.swift b/NotedTests/Test Objects/TestNote.swift index a46d207..5b074a6 100644 --- a/NotedTests/Test Objects/TestNote.swift +++ b/NotedTests/Test Objects/TestNote.swift @@ -9,14 +9,14 @@ import Foundation import Noted -enum TestNote { +enum TestNote: NoteType { case Test } -extension TestNote: NoteType { - static var testTriggerAction: ((AnyObject) -> Void)? +enum TestContextNote: NoteType { + case Test - func trigger(_ receiver: AnyObject) { - TestNote.testTriggerAction?(receiver) + var context: AnyObject? { + return "test context" as AnyObject } } diff --git a/NotedTests/Test Objects/TestObserver.swift b/NotedTests/Test Objects/TestObserver.swift index 1c0d151..2598fc5 100644 --- a/NotedTests/Test Objects/TestObserver.swift +++ b/NotedTests/Test Objects/TestObserver.swift @@ -10,5 +10,9 @@ import Foundation import Noted class TestObserver: NoteObserver { - func didReceive(note: NoteType) { } + var noteAction: ((NoteType) -> Void)? + + func didReceive(note: NoteType) { + noteAction?(note) + } } From c944fed6d2385fc5c5df251c73a5c9b5c5c1469c Mon Sep 17 00:00:00 2001 From: Dominik Hadl Date: Sat, 17 Sep 2016 17:01:33 +0200 Subject: [PATCH 08/10] Remove context from NoteType --- Noted/Classes/NoteType.swift | 10 +--------- NotedTests/NotedTests.swift | 1 - NotedTests/Test Objects/TestNote.swift | 8 -------- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/Noted/Classes/NoteType.swift b/Noted/Classes/NoteType.swift index a81d4f1..2bd637f 100644 --- a/Noted/Classes/NoteType.swift +++ b/Noted/Classes/NoteType.swift @@ -8,12 +8,4 @@ import Foundation -public protocol NoteType { - var context: AnyObject? { get } -} - -extension NoteType { - public var context: AnyObject? { - return nil - } -} +public protocol NoteType { } diff --git a/NotedTests/NotedTests.swift b/NotedTests/NotedTests.swift index 564a516..d00bd84 100644 --- a/NotedTests/NotedTests.swift +++ b/NotedTests/NotedTests.swift @@ -55,7 +55,6 @@ class NotedTests: XCTestCase { let test = note as? TestNote XCTAssert(test == TestNote.Test, "The receiver of the notification should be the observer.") - XCTAssertNil(test?.context, "Default note context should be nil .") expect.fulfill() } diff --git a/NotedTests/Test Objects/TestNote.swift b/NotedTests/Test Objects/TestNote.swift index 5b074a6..372362f 100644 --- a/NotedTests/Test Objects/TestNote.swift +++ b/NotedTests/Test Objects/TestNote.swift @@ -12,11 +12,3 @@ import Noted enum TestNote: NoteType { case Test } - -enum TestContextNote: NoteType { - case Test - - var context: AnyObject? { - return "test context" as AnyObject - } -} From c06a6cce7a64a9fffb677e8f0267dfbe93b109f3 Mon Sep 17 00:00:00 2001 From: Dominik Hadl Date: Sat, 17 Sep 2016 17:29:26 +0200 Subject: [PATCH 09/10] Update README --- README.md | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 128 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fcc5187..c402833 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,143 @@ -

-Noted -

-Notifications made simpler. +# Noted + +A minimalistic and effective replacement for `NSNotificationCenter`, that promotes the observer pattern and keeps weak references to it's observers. + +**Features:** + +* Simple Setup +* Use `enum` As Notification +* Weak References +* No Unregister Necessary +* OSS, Free, Maintained, No Dependencies ## 📝 Requirements * iOS 8.0+ -* Swift 2.0+ +* Swift 3.0+ ## 📦 Installation ### Carthage ~~~ -github "nodes-ios/Noted" +github "nodes-ios/Noted" "~> 1.0" ~~~ +## 💻 Usage + +Noted itself is a singleton that you can access from anywhere in your app, and might be especially useful for easily sending messages to your router and/or any other class. + +Start with: + +```swift +import Noted +``` + +### Protocols + +There are two protocols you need to implement in order for *Noted* to work. One for the notification (object/structure/enum) a.k.a `Note` and second for each observer you want to add. + +**Note Type** + +Just make your object conform to it and that's it! + +We recommend using `enum`s as you get most out of Swift and you can associate values without wrapping them in some ugly dictionary with string keys. + +```swift +enum Note: NoteType { + case ShowLoginFlow + case SignupForPush(userId: Int) +} +``` + +**Note Observer** + +The observer protocol requires you to implement a function `didReceive(note: NoteType)` that will be called each time a notification is sent and your observer is add as and observer. + +> **NOTE:** This function will be executed on the main thread. + +```swift +class ViewController: UIViewController, NoteObserver { + // MARK: - Note Observer - + func didReceive(note: NoteType) { + switch note { + case .SingupForPush(let userId): + print(userId) + } + } +} +``` + +### Actions + +There are three actions Noted supports: + +* Add Observer +* Remove Observer* +* Post Note + +> *All observers have a weak reference in Noted, so you are not required to manually remove observers if you want them to disappear on deinit. + +#### Adding Observer + +```swift +func viewDidLoad() { + super.viewDidLoad() + Noted.defaultInstance.add(observer: self) +} + +``` + +> **NOTE:** You need to keep a strong reference to the observer, otherwise it will get removed immediately after adding. + +#### Removing Observer + +```swift +func viewWillDisappear() { + super.viewWillDisappear() + Noted.defaultInstance.remove(observer: self) +} + +``` + +#### Posting Notes + +Posting notes is as simple as it could get, simply call `post(note: note)` and it will be send momentarily. + +```swift +func buttonPressed() { + Noted.defaultInstance.post(note: Note.SignupForPush(userId: 1)) +} +``` + + +> **NOTE:** Be advised that Noted uses a background thread and synchronizes post action with addittion and removal of observers. + +### Advanced Features + +If you send a lot of notifications and have many observers, this section might be useful for you. + +#### Filtering + +Noted offers an option to filter which observers will receive a notification and by default uses a `PassthroughNoteFilter` that let's all notifications come through. + +You can override this behaviour by providing your own `NoteFilter` on the observer. + +```swift +struct RandomFilter: NoteFilter { + func shouldFilter(note: NoteType) -> Bool { + return arc4random() % 10 < 5 + } +} + +class TestObserver: NoteObserver { + let noteFilter: NoteFilter = RandomFilter() + + func didReceive(note: NoteType) { } +} +``` + +> **NOTE:** Do not use the filter provided above unless you have #courage. ## 👥 Credits Made with ❤️ at [Nodes](http://nodesagency.com). From fd729a12970ddabf6b6a4f64d8e0287db53cd9b5 Mon Sep 17 00:00:00 2001 From: Jakob Mygind Date: Mon, 19 Sep 2016 11:40:14 +0200 Subject: [PATCH 10/10] updated readme --- README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c402833..e642a4b 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,13 @@ A minimalistic and effective replacement for `NSNotificationCenter`, that promot ### Carthage ~~~ -github "nodes-ios/Noted" "~> 1.0" +github "nodes-ios/Noted" " ~> 2.0" +~~~ + +For latest Swift 2 version use: + +~~~ +github "nodes-ios/Noted" " ~> 1.0" ~~~ ## 💻 Usage @@ -41,7 +47,7 @@ There are two protocols you need to implement in order for *Noted* to work. One Just make your object conform to it and that's it! -We recommend using `enum`s as you get most out of Swift and you can associate values without wrapping them in some ugly dictionary with string keys. +We recommend using `enum`s as you get most out of Swift and you can associate values without wrapping them in some ugly dictionary with string keys. ```swift enum Note: NoteType { @@ -102,7 +108,7 @@ func viewWillDisappear() { #### Posting Notes -Posting notes is as simple as it could get, simply call `post(note: note)` and it will be send momentarily. +Posting notes is as simple as it could get, simply call `post(note: note)` and it will be send momentarily. ```swift func buttonPressed() { @@ -111,11 +117,11 @@ func buttonPressed() { ``` -> **NOTE:** Be advised that Noted uses a background thread and synchronizes post action with addittion and removal of observers. +> **NOTE:** Be advised that Noted uses a background thread and synchronizes post action with addittion and removal of observers. ### Advanced Features -If you send a lot of notifications and have many observers, this section might be useful for you. +If you send a lot of notifications and have many observers, this section might be useful for you. #### Filtering @@ -132,7 +138,7 @@ struct RandomFilter: NoteFilter { class TestObserver: NoteObserver { let noteFilter: NoteFilter = RandomFilter() - + func didReceive(note: NoteType) { } } ```