The optional type is newly introduced in Swift and is very powerful. What is discussed in this blog post is how to ensure the security of strong types through optional types in Swift. As an example, let's create a Swift version of the Objective-C API, but in fact Swift itself does not need such an API.
Add objectsForKeys function to Dictionary
In Objective-C, NSDictionary has a method - objectsForKeys:NoFoundMarker:, This method takes an NSArray array as a key-value parameter, and then returns an array containing the relevant values. The document says: "Return the Nth value in the array, corresponding to the Nth value in the input array", what if there is a key value that does not exist in the dictionary? So there is a notFoundMarker as the return prompt. For example, if the third key value is not found, then the third value in the return array is this notFoundMarker, not the third value in the dictionary. However, this value is only used to remind that no corresponding value was found in the original dictionary, but the element exists in the return array, and the notFoundMarker is used as the placeholder. Because this object cannot be used directly, there is a special class in the Foundation framework to handle this situation: NSNull.
In Swift, the Dictionary class does not have a function similar to objectsForKeys. To illustrate the problem, we add one and make it a common way to manipulate dictionary values. We can use extension to implement:
extension Dictionary{
func valuesForKeys(keys:[K], notFoundMarker: V )->[V]{
//The specific implementation code will be written later
}
}
The above is the Swift version we implemented, which is very different from the Objective-C version. In Swift, because of its strong type, the returned result array can only contain elements of a single type, so we cannot put NSNull in the string array, but Swift has a better choice, and we can return an optional type of data. All our values are enclosed in optional types, not NSNull, we just use nil.
extension Dictionary{
func valuesForKeys(keys: [Key]) -> [Value?] {
var result = [Value?]()
()
for key in keys{
(self[key])
}
return result
}
}
Easier ways to do it in Swift
Friends may ask, why is there no need to implement such an API in Swift? In fact, it has a simpler implementation, as shown in the following code:
extension Dictionary {
func valuesForKeys(keys: [Key]) -> [Value?] {
return { self[$0] }
}
}
The functions implemented in the above method are the same as those implemented in the initial method. Although the core function is to encapsulate map calls, this example also illustrates why Swift does not provide a lightweight API interface, because friends can implement it simply by calling maps.
Next, let’s experiment with a few examples:
var dic: Dictionary = [ "1": 2, "3":3, "4":5 ]
var t = (["1", "4"])
//The result is: [Optional(2), Optional(5)]
var t = (["3", "9"])
// The result is: [Optional(3), nil]
t = ([])
//The result is: []
Inline optional types
Now, if we call the last method for each result, see what the result is?
var dic: Dictionary = [ "1": 2, "3":3, "4":5 ]
var t = (["1", "4"]).last //The result is: Optional(Optional(5))
// Optional(Optional("Ching"))
var t = (["3", "9"]).last
// The result is: Optional(nil)
var t = ([]).last
// The result is: nil
The friends immediately became confused. Why did the optional types included in the two layers appear? , especially for the second case Optional(nil), what rhythm is this?
Let's look back at the definition of the last property:
var last:T? { get }
It is obvious that the type of the last attribute is an optional type of the array element type. In this case, because the element type is (String?), then combined with the returned type, the result is String??, which is the so-called nested optional type. But what does the essence of nested optional types mean?
If we re-call the above method in Objective-C, we will use NSNull as the placeholder, and the calling syntax of Objective-C looks like this:
[dict valuesForKeys:@[@"1", @"4"] notFoundMarker:[NSNull null]].lastObject
// 5
[dict valuesForKeys:@[@"1", @"3"] notFoundMarker:[NSNull null]].lastObject
// NSNull
[dict valuesForKeys:@[] notFoundMarker:[NSNull null]].lastObject
// nil
Whether it is the Swift version or the Objective-C version, the return value of nil means that the array is empty, so it does not have the last element. However, if the return is Optional(nil) or NSNull in Objective-C, it means that the last element in the array exists, but the content of the element is empty. In Objective-C, NSNull can only be used as a placeholder to achieve this goal, but Swift can be implemented from the perspective of language system type.
Provide a default value
Further encapsulation, what if one or some elements in my dictionary do not exist, we want to provide a default value? The implementation method is very simple:
extension Dictionary {
func valuesForKeys( keys:[Key], notFoundMarker: Value)->[Value]{
return (kes).map{ $0 ?? notFoundMarker }
}
}
(["1", "5"], notFoundMarker: "Anonymous")
Compared with Objective-C, it requires placeholders to achieve placeholding, but Swift has already supported this usage natively from the level of the language type system, and also provides rich syntax functions. This is how powerful Swift optional types are. At the same time, note that the null-contract operator is used in the above example.