Declan McKenna
Declan McKenna

Reputation: 4870

How can I create a static computed property that computes only once?

I've created a computed static property for which I'd like to have it computed once and whenever accessed after then will just return the original computed value. lazy properties appear to be what I want but after a bit of searching it appears static properties are lazy by default.

When I run the following code it comes out fine the first time it is accessed/ if I run each unit test individually. However when I access this a 2nd time I'm given an empty array.

static var att: [ConferenceNumber] = {
    let list = buildDirectory(for: .att, from: jsonArray)
    return list
}()

private static let jsonArray: [Any]? = {
    let numbersFilePath = Bundle.main.path(forResource: "numbers", ofType:"json")
    let data = try! Data(contentsOf: URL(fileURLWithPath:numbersFilePath!), options: .uncached)

    return try! JSONSerialization.jsonObject(with: data) as? [Any]
}()

Code where this is called a 2nd time and returns an empty array

private static func isAttMeeting(meetingText: String, parsedPhoneNumbers: [String]) -> Bool {
    let attPhoneNumbers = ConferenceNumberDirectory.att.map{$0.number}
    let attNumberWasParsed = parsedPhoneNumbers.intersects(with: attPhoneNumbers)
    if attNumberWasParsed {
        return true
    }
    return meetingText.contains(pattern: attURLRegex) || meetingText.contains(pattern: attURLRegex2)
}

Upvotes: 5

Views: 5473

Answers (1)

André Slotta
André Slotta

Reputation: 14030

Your solution should work. Maybe there is something wrong with some other part of your code. Take a look at the following example:

var myStrings = ["a", "b"]

class SomeClass {
    static let strings: [String] = {
        return myStrings
    }()
}

print("myStrings: \(myStrings)")
print("static strings: \(SomeClass.strings)")

myStrings.append("c")

print("myStrings: \(myStrings)")
print("static strings: \(SomeClass.strings)")

Prints:

myStrings: ["a", "b"]
static strings: ["a", "b"]
myStrings: ["a", "b", "c"]
static strings: ["a", "b"]

So for you the following piece of code should work:

class ConferenceNumberDirectory {
    static let att: [ConferenceNumber] = {
        return buildDirectory(for: .att, from: jsonArray)
    }()
}

Upvotes: 7

Related Questions