Skip to content

Instantly share code, notes, and snippets.

@DanielCardonaRojas
Created May 30, 2019 14:56
Show Gist options
  • Save DanielCardonaRojas/da0d9e79c4a99d6c1c50bfaf9bdfa4d5 to your computer and use it in GitHub Desktop.
Save DanielCardonaRojas/da0d9e79c4a99d6c1c50bfaf9bdfa4d5 to your computer and use it in GitHub Desktop.
Cached computed properties
struct Cached <V, C> {
typealias Computation = (V) -> C
private var computation: (V) -> C
private var cachedResult: C?
var value: V {
willSet {
cachedResult = nil
}
}
var computed: C {
mutating get {
if cachedResult == nil {
cachedResult = computation(value)
}
return cachedResult!
}
}
init(_ value: V, _ computation: @escaping (V) -> C) {
self.computation = computation
self.value = value
}
// MARK: Functor
public func map<D>(_ f: @escaping (C) -> D) -> Cached<V,D> {
return Cached<V,D>(self.value) { f(self.computation($0)) }
}
}
@DanielCardonaRojas
Copy link
Author

Many times you will have code like this

class ViewController: UIViewController {
     var jsonString: String  = "..."
     var parsedName: String {
         // parse json
     }
}

Every time you use the parsedName property parsing will be done over and over again. This abstraction caches the json so only when it changes parsedName will be re computed.

@DanielCardonaRojas
Copy link
Author

DanielCardonaRojas commented May 30, 2019

Idea: Remove the first type parameter and limit to reference types that are KVO compliant, this way we don't have to set the value manually.

class Cached <V, C>: NSObject {
    typealias Computation = (V) -> C
    private var computation: (V) -> C
    private var cachedResult: C?
    private var observation: NSKeyValueObservation?
    
    var value: V {
        willSet {
            cachedResult = nil
        }
    }
    
    var computed: C {
        get {
            if cachedResult == nil  {
                cachedResult = computation(value)
            }
            return cachedResult!
        }
    }
    
    init(_ value: V, _ computation: @escaping Computation) {
        self.computation = computation
        self.value = value
        super.init()
    }
    
    init<K: NSObject>(_ kvo: K, path: KeyPath<K, V>, computation: @escaping Computation) {
        self.computation = computation
        self.value = kvo[keyPath: path]
        super.init()
        kvo.observe(path, changeHandler: { owner, change in
            self.updateValue(owner[keyPath: path])
        })
    }
    
    private func updateValue(_ val: V) {
        self.value = val
    }
    
    // MARK: Functor
    public func map<D>(_ f: @escaping (C) -> D) -> Cached<V,D> {
        return Cached<V,D>(self.value) { f(self.computation($0)) }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment