I am using the following pattern in my code. It works. But, it feels as though it is on the edge of what should work!

Should I consider an alternative?

I have an array in my class that maps an underlying data array. This is the sort of thing for which I could use a lazy sequence. However, I don't want to re-evaluate every time an element is accessed. So, I lazily load my array, then track the data array with KVO. I also want my top-level array to be observable. I mutate that with a mutableArrayValue.

I have mocked up a simple working example of the pattern below.

mutableArrayValue would usually cause a lazy var to initialise, but it seems OK to call it during an initialisation.

Setting up an observation on a lazy var does not cause it to initialise. I don't get a call here even though the effective setting of the array comes after the observation is created.

import Cocoa

class LazyData: NSObject
    @objc dynamic lazy var units: [Int] = {
        print("Loading units")
        return [1,2,3,4,5,6] }()

class Lazy: NSObject
    let data: LazyData

    init(with data: LazyData) {
        self.data = data

    var unitsObservation: NSKeyValueObservation?

    func tenTimes(_ input: Int) -> Int { return 10 * input }

    @objc dynamic lazy var tens: [Int] = {
        print("Loading tens")

        let tenProxy = mutableArrayValue(forKeyPath: #keyPath(Lazy.tens))

        unitsObservation = data.observe(\.units) { object, change in
            if change.kind == .insertion {
                for insertionIndex in change.indexes! {
                    let ten = self.tenTimes(object.units[insertionIndex])
                    tenProxy.insert(ten, at: insertionIndex)

        return data.units.map { tenTimes($0) }

var lazyData = LazyData()
var lazy = Lazy(with: lazyData)


let lazyDataProxy = lazyData.mutableArrayValue(forKeyPath: #keyPath(LazyData.units))
lazyDataProxy.insert(9, at: 0)

  • \$\begingroup\$ Actually, as written this doesn't work if you want to subsequently observe the tens property. mutableArrayValue(forKey... must be called after any observations are added to a key for those observations to be fired. So, that call should be within the the observation closure. \$\endgroup\$ – Giles Jun 13 at 8:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.