This question asks whether one can use subscripting with CKRecord in Swift. While I already knew how to do what the questioner wanted, every permutation of it gives me a stack overflow:
subscript(key: String) -> CKRecordValue? {
get {
return objectForKey(key) as CKRecordValue?
}
set {
setObject(newValue, forKey: key)
}
}
The stack overflow occurs in the getter. (I've never tried the setter, so it may occur there, too.) I've tried implementing with objectForKey:, objectForKeyedSubscript:, and valueForKey:. All produce the same result: a stack overflow.
This is very strange, since CKRecord is certainly written in Objective-C. Why would it recursively call Swift's subscript method? It makes no sense. Nate Cook, in his answer to the questioner, wonders why Swift doesn't bridge objectForKeyedSubscript: automatically. Well, maybe the code to do that is not fully baked, but is causing this problem. I will have to try it with another class that has objectForKeyedSubscript:.
UPDATE
It appears that objectForKeyedSubscript: is ordinarily bridged. I created a class in Objective-C with the appropriate methods, added it to the bridging header, and the indexers were there and compiled without issue. Even better, it worked without a stack overflow.
This means that something very unusual is going on with CKRecord.
A THEORY
If you create a class in Swift that descends from NSObject and implements the subscript method on it with a String as the key, this becomes objectForKeyedSubscript:. (For "pure Swift" classes, I suspect this is not the case.) You can verify this by importing your Swift class into Objective-C and verifying that objectForKeyedSubscript: is there.
Since CKRecord descends from NSObject, implementing subscript overrides the default implementation. Further, it seems that objectForKey: and valueForKey: all ultimately called objectForKeyedSubscript:, which results in (read: "is the same as") a call to subscript, which causes the stack overflow.
That may explain why the stack overflow occurs. It still does not explain why objectForKeyedSubscript: was not automatically bridged, but perhaps it's because the definition of setObject:forKeyedSubscript: has a slightly different type signature from the canonical one: - (void)setObject:(id <CKRecordValue>)object forKeyedSubscript:(NSString *)key;. This makes no difference to Objective-C, but might trip up the "bridging code". Swift is pretty new, after all.
After some testing and debugging (via a subclass), I discovered that, for
CKRecord,objectForKey:does indeed callobjectForKeyedSubscript:. Also, implementingsubscriptin a Swift class that is marked@objcimplicitly (by descending fromNSObject) or explicitly means thatsubscriptis implemented asobjectForKeyedSubscript:.This means that implementing
subscriptonCKRecordin an extension hides the default implementation, which causes the stack overflow.