# Swift lazy observe and mutate pattern for arrays

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] = {
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] = {

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)

print(lazy.tens)

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

print(lazy.tens)

• 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. – Giles Jun 13 at 8:28