-
Notifications
You must be signed in to change notification settings - Fork 41
/
Contents.swift
305 lines (225 loc) · 7.38 KB
/
Contents.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
import Foundation
protocol Notification: AnyObject {
var data: Any? { get }
}
protocol Observer: AnyObject {
func notify(with notification: Notification)
}
class Subject {
// MARK: - Inner classes
private final class WeakObserver {
// MARK: - Properties
weak var value: Observer?
// MARK: - Initializers
init(_ value: Observer) {
self.value = value
}
}
// MARK: - Properties
private var observers = [WeakObserver]()
private var queue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
// MARK: - Subscripts
/// Convenience subscript for accessing observers by index, returns nil if there is no object found
public subscript(index: Int) -> Observer? {
get {
guard index > -1, index < observers.count else {
return nil
}
return observers[index].value
}
}
/// Searches for the observer and returns its index
public subscript(observer: Observer) -> Int? {
get {
guard let index = observers.firstIndex(where: { $0.value === observer }) else {
return nil
}
return index
}
}
// MARK: - Methods
func add(observer: Observer) {
// .barrier flag ensures that within the queue all reading is done before the below writing is performed and pending readings start after below writing is performed
queue.sync(flags: .barrier) {
self.observers.append(WeakObserver(observer))
}
}
/// Removes an Observer. The difference between dispose(observer:) and this method is that this method immediately removes an observer.
///
/// - Parameter observer: is an Observer to be removed immediately
func remove(observer: Observer) {
queue.sync(flags: .barrier) {
guard let index = self[observer] else {
return
}
self.observers.remove(at: index)
}
}
/// Sends notification to all the Observers. The method is executed synchronously.
///
/// - Parameter notification: is a type that conforms to Notification protocol
func send(notification: Notification) {
queue.sync {
recycle()
for observer in observers {
observer.value?.notify(with: notification)
}
}
}
/// Disposes an observer. The difference between remove(observer:) and this method is that this method delays observer removal until the recycle method will be called (the next time when a new Notification is sent)
///
/// - Parameter observer: is an Observer to be disposed
func dispose(observer: Observer) {
queue.sync(flags: .barrier) {
if let index = self[observer] {
observers[index].value = nil
}
}
}
// MARK: - Private
private func recycle() {
for (index, element) in observers.enumerated().reversed() where element.value == nil {
observers.remove(at: index)
}
}
}
// Removal of Observer
infix operator --=
// Disposal of Observer
infix operator -=
extension Subject {
static func +=(lhs: Subject, rhs: Observer) {
lhs.add(observer: rhs)
}
static func +=(lhs: Subject, rhs: [Observer]) {
rhs.forEach { lhs.add(observer: $0) }
}
static func --=(lhs: Subject, rhs: Observer) {
lhs.remove(observer: rhs)
}
static func --=(lhs: Subject, rhs: [Observer]) {
rhs.forEach { lhs.remove(observer: $0) }
}
static func -=(lhs: Subject, rhs: Observer) {
lhs.dispose(observer: rhs)
}
static func -=(lhs: Subject, rhs: [Observer]) {
rhs.forEach { lhs.dispose(observer: $0) }
}
static func ~>(lhs: Subject, rhs: Notification) {
lhs.send(notification: rhs)
}
static func ~>(lhs: Subject, rhs: [Notification]) {
rhs.forEach { lhs.send(notification: $0) }
}
}
//: Usage:
class ObserverOne: Observer {
func notify(with notification: Notification) {
print("Observer One: " , notification)
}
}
class ObserverTwo: Observer {
func notify(with notification: Notification) {
print("Observer Two: " , notification)
}
}
class ObserverThree: Observer {
func notify(with notification: Notification) {
print("Observer Three: " , notification)
}
}
final class EmailNotification: Notification {
// MARK: - Conformance to Notification protocol
var data: Any?
// MARK: - Initializers
init(message: String) {
data = message
}
}
extension EmailNotification: CustomStringConvertible {
var description: String {
return "data: \(data as Any)"
}
}
let observerOne = ObserverOne()
var observerTwo: ObserverTwo? = ObserverTwo()
let observerThree = ObserverThree()
let subject = Subject()
subject += [observerOne, observerTwo!, observerThree]
subject ~> EmailNotification(message: "Hello Observers, this messag was sent from the Subject instance!")
subject.send(notification: EmailNotification(message: "Hello Observers, this messag was sent from the Subject instance!"))
let notificationOne = EmailNotification(message: "Message #1")
let notificationTwo = EmailNotification(message: "Message #2")
let notificationThree = EmailNotification(message: "Message #3")
subject ~> [notificationOne, notificationTwo, notificationThree]
subject --= observerThree
subject ~> notificationOne
observerTwo = nil
print("Observer Two was set to nil: ", observerTwo as Any)
subject ~> [notificationOne, notificationTwo, notificationThree]
//: Experimental code:
/*
public final class Disposable {
private let recycle: () -> ()
init(_ recycle: @escaping () -> ()) {
self.recycle = recycle
}
deinit {
recycle()
}
public func add(to bag: DisposeBag) {
bag.insert(self)
}
public func dispose() {
recycle()
}
}
public class DisposeBag {
/// Keep a reference to all Disposable instances -> this is the actual bag
private let disposables = Atomic<[Disposable]>([])
/// To be able to create a bag
public init() {}
/// Called by a Disposable instance
fileprivate func insert(_ disposable: Disposable) {
disposables.apply { _disposables in
_disposables.append(disposable)
}
}
/// Clean everything when the bag is deinited by calling dispose()
/// on all Disposable instances
deinit {
disposables.apply { _disposables in
_disposables.forEach { $0.dispose() }
}
}
}
private class UnfairLock {
private let _lock: os_unfair_lock_t
fileprivate init() {
_lock = .allocate(capacity: 1)
_lock.initialize(to: os_unfair_lock())
}
fileprivate func lock() {
os_unfair_lock_lock(_lock)
}
fileprivate func unlock() {
os_unfair_lock_unlock(_lock)
}
deinit {
_lock.deallocate()
}
}
internal class Atomic<Value> {
private let lock = UnfairLock()
private var _value: Value
internal init(_ value: Value) {
_value = value
}
internal func apply(_ action: (inout Value) -> Void) {
lock.lock()
defer { lock.unlock() }
action(&_value)
}
}
*/