I'm attempting to override the default subscript function for a custom Dictionary type. Certain keys should never have certain types of values, so I want to allow/deny values in the subscript setter.
(This may be an XY Problem, so I'm interested in alternative approaches, but this question still stands.)
The subscript extension appears to work fine for the setter, as updateValue(_:forKey:)
can be used instead of the subscript setter (which would otherwise cause infinite recursion).
struct MyData {
var isBadData: Bool
}
extension Dictionary where Key == String, Value == MyData {
subscript(key: Key) -> Value? {
// Omitting getter results in ERROR: Subscript with a setter must also have a getter
get {
return self[key] // WARNING: Function call causes an infinite recursion
// QUESTION: is there a non-subscript getter alternative?
}
set(newValue) {
if let newValue, newValue.isBadData {
return // do not add bad data to dictionary
} else if let newValue {
//self[key] = newValue // WARNING: Function call causes an infinite recursion
updateValue(newValue, forKey: key) // non-subscript setter alternative works
} else {
removeValue(forKey: key)
}
}
}
}
But for the getter, I don't know how to get
the value from the dictionary without recursively calling the subscript getter.
NSDictionary has value(forKey:)
but there does not appear to be a similar non-subscript alternative in the Swift Dictionary docs.
How can I get a Dictionary value without using the subscript? Or is there a way to somehow call super
on the subscript to use the original implementation?
You can use the Dictionary.Index
-based subscript.
get {
if let i = index(forKey: key) {
return self[i].value
}
return nil
}
That said, you should not write an extension
of Dictionary
to change its existing functionality. This is not the purpose of extensions, and you might surprise users of your code by doing something they don't expect. There are many other ways that someone can insert data into a dictionary (e.g. updateValue
as you have shown), and it would be impractical to override all of those.
You should create your own Dictionary
wrapper type, or add a subscript with an argument label, so that it is distinguishable from the built-in subscript.
subscript(noBadData key: Key) -> Value?