Fred Faust
Fred Faust

Reputation: 6790

Property observer for generic class

I've created a wrapper for a dictionary, I'd like to use the didSet property observer on instances of it. I can get didSet from the private dictionary but am not sure how to define it within the wrapper class.

I would like to pass the oldValue from the Swift dictionary as well, here's the class:

public class SynchronizedDictionary<K: Hashable,V> {

private var dictionary: [K:V] = Dictionary() { didSet {  } }
private let accessQueue: dispatch_queue_t
public var count: Int {

    get {

        var count: Int!
        dispatch_sync(self.accessQueue) { () -> Void in

            count = self.dictionary.count
        }

        return count
    }

}

init() {

    let qosClassUserInit                = QOS_CLASS_USER_INITIATED
    let newConcurrentQueueAttributes    = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, qosClassUserInit, 0)
    let newConcurrentQueue              = dispatch_queue_create("SynchronizedDictionaryAccess", newConcurrentQueueAttributes)
    self.accessQueue = newConcurrentQueue

}

public subscript(index: K) -> V? {

    set {

        dispatch_barrier_async(self.accessQueue) {

            self.dictionary[index] = newValue
        }

    }

    get {

        var element: V?
        dispatch_sync(self.accessQueue) {

            element = self.dictionary[index]
        }

        return element
    }

}

/// Removes the value for the given key and the key.
public func removeValueForKey(key: K) {

    dispatch_barrier_async(self.accessQueue) { () -> Void in

        if self.dictionary[key] != nil {

            self.dictionary.removeValueForKey(key)
        }

    }

}

/// Returns the dictionary values as an array.
public func values() -> [V] {

    var values = [V]()

    dispatch_sync(self.accessQueue) { () -> Void in

        values = Array(self.dictionary.values)
    }

    return values
}

public func removeAll() {

    dispatch_barrier_async(self.accessQueue) { () -> Void in

        self.dictionary.removeAll()
    }

}

public func doesObjectExistsForKey(key: K) -> Bool {

    var value: V?
    dispatch_sync(self.accessQueue) { () -> Void in

        value = self.dictionary[key]
    }

    return value != nil ? true : false

}

}

Upvotes: 0

Views: 366

Answers (1)

dfrib
dfrib

Reputation: 73186

You could use a delegate: called from a property observer observing updates of the private dictionary in SynchronizedDictionary, in turn triggering a callback to some method in the class where you work with a SynchronizedDictionary object. Below follows an example of this procedure applied to a simplified version of your custom dictionary class.

Note that the property observer (I'm using willSet for convenience) is set to a tuple which in turn is used to update the private dictionary, rather than trying to observe changes in the private dictionary itself (the latter would make it tricky to make sense out of newValue in the willSet property observer).

public protocol MyDictionaryDelegate {
    typealias MyKey
    typealias MyValue
    func existingDictionaryEntryWasUpdated(oldValue: MyValue, newPair: (MyKey, MyValue))
}

public class MinimalSynchronizedDictionary<K: Hashable, V: Comparable, T: MyDictionaryDelegate where T.MyKey == K, T.MyValue == V> {

    private var updateDictionaryWithPair : (K, V) {
        /* Note, willSet not called prior to update in initializer, so we can
           use a dummy pair to begin with, OK */
        willSet {

            /* update existing dict. entry */
            if let oldValue = dictionary[newValue.0] where oldValue != newValue.1 {
                dictionary.updateValue(newValue.1, forKey: newValue.0)
                delegate?.existingDictionaryEntryWasUpdated(oldValue, newPair: newValue)
            }
            /* new dict. entry or same value update */
            else {
                dictionary.updateValue(newValue.1, forKey: newValue.0)
            }
        }
    }
    private var dictionary: [K:V] = Dictionary()

    var delegate: T?

    init(dummyInitialDictPair: (K, V)) {
        updateDictionaryWithPair = dummyInitialDictPair
    }

    internal func updateDictionaryWithPair(newPair newPair: (K, V)) {
        updateDictionaryWithPair = newPair
    }
}

Note that I've type constrained generic V to protocol Comparable for this example (to make use of != operator in the willSet clause), which you can leave out if you don't plan on comparing the dictionary values against each other.

Now, example class conforming to MyDictionaryDelegate, containing a MinimalSynchronizedDictionary object and receiving delegate callbacks from the objects delegate.

class MyOtherClass : MyDictionaryDelegate {
    typealias MyKey = String
    typealias MyValue = Int

    var syncDict : MinimalSynchronizedDictionary<MyKey, MyValue, MyOtherClass>

    init(syncDict: MinimalSynchronizedDictionary<MyKey, MyValue, MyOtherClass>) {
        self.syncDict = syncDict
        self.syncDict.delegate = self
    }

    // MARK: MyDictionaryDelegate
    func existingDictionaryEntryWasUpdated(oldValue: MyValue, newPair: (MyKey, MyValue)) {
        print("Dictionary entry for key '\(newPair.0)' was updated from old value '\(oldValue)' to new value '\(newPair.1)'.")
    }
}

let myDict = MinimalSynchronizedDictionary<String, Int, MyOtherClass>(dummyInitialDictPair: ("",0))
let myMainClass = MyOtherClass(syncDict: myDict)

myDict.updateDictionaryWithPair(newPair: ("World", 1))
myDict.updateDictionaryWithPair(newPair: ("Hello", 1))
myDict.updateDictionaryWithPair(newPair: ("World", 2))
    /* Prints: Dictionary entry for key 'World' was updated 
               from old value '1' to new value '2'.         */

myMainClass.syncDict.updateDictionaryWithPair(newPair: ("World", 2))
    /* (key exists, but same value --> no update)           */
myMainClass.syncDict.updateDictionaryWithPair(newPair: ("World", 3))
    /* Prints: Dictionary entry for key 'World' was updated 
               from old value '2' to new value '3'.         */

Upvotes: 2

Related Questions