This is the 18th day of my participation in Gwen Challenge

Here’s what I learned from reading [SwiftyJSON] source code.

Other articles

SwiftyJSON (1)

Literal conversion

In SwiftyJSON, there are rich initialization methods for JSON that can be used to quickly create a JSON literal. Such as:

let json = JSON(12345)
let json = JSON(["name"."age"])
let json = JSON(["name":"Jack"."age": 25])
Copy the code

Swift provides a very interesting set of interfaces for converting literals to corresponding types. After these conversion interfaces are implemented, it is easy to convert to the corresponding type by way of assignment. Literal conversion interfaces all define a TypeAlias and the corresponding init method.

Frequently used interfaces are:

  • ExpressibleByStringLiteral: one definitionpublic typealias StringLiteralType = String, and the corresponding init method.
  • ExpressibleByArrayLiteral
  • ExpressibleByBooleanLiteral
  • ExpressibleByDictionaryLiteral
  • ExpressibleByFloatLiteral
  • ExpressibleByNilLiteral
  • ExpressibleByIntegerLiteral

For more on literal conversions, I recommend reading Wang Wei’s article: Literal conversions, which have some outdated content, but still have a lot to learn.

Literal conversions are a double-edged sword. They can shorten the code and simplify the meaning, but at the same time, the lack of an obvious initialization function can confuse others and make it impossible to quickly jump to the corresponding initialization method via CMD + click.

How do I add nil values to a Swift dictionary

It’s strange to see this code in SwiftyJSON:

guard let dict = object as? [String: Any?] else {
        return nil
}
let body = try dict.keys.map { key throws -> String in
    guard let value = dict[key] else {
            return "\"\(key)\": null"
    }
    guard let unwrappedValue = value else {
            return "\"\(key)\": null"
    }

    let nestedValue = JSON(unwrappedValue)
    / /...
}
        
Copy the code

[String: Any?] Do I need to unpack the key twice to obtain the value of a key in the dictionary of type? Can value be nil? When you assign a key to nil, doesn’t that just erase the key?

Take a look at the following code:

let value: Any? = nil
var dict = [String: Any?]()
dict["key"] = value
debugPrint(dict)

// output: ["key": nil]
Copy the code

Indeed as expected/String: Any? The value in the dictionary of a type can be null. Let’s look at the code below. It’s not true that any key assigned to nil can be stored in a dictionary.

let aKey = "aKey"
var dict = [String: Any?] ()let value: String? = nil

dict[aKey] = value
debugPrint(dict)
// Output: [:]

dict[aKey] = value as Any?
debugPrint(dict)
// output: ["aKey": nil]
Copy the code

Only value can be Any? Type is saved in the dictionary.

How do I add nil to a Swift dictionary?

Method 1: Match the corresponding type

The same way you would add any other type of value to a dictionary. But make sure the dictionary can hold the corresponding value type. For a dictionary whose value type is optional, such as [String: Any? , so it can hold nil values. For example,

let x : [String : AnyObject? ]= ["foo" : nil]
Copy the code

Note that a [K:V] dictionary, when accessed by subscript, returns an optional V? , optional indicates whether there is a value corresponding to the key. If there is a value, the corresponding value is returned. When setting a value by subscripting, you can set a value or delete an entry by setting nil.

Therefore, for [String: Any? A dictionary of type. The type returned by subscript is Any?? . Similarly, when putting values into subscripts, external options allow you to set values or delete entries (external options are Any?? The second?) . If we simply write:

x["foo"] = nil
Copy the code

The compiler concludes that it is Any?? Type nil, externally optional means deleting the entry for key foo.

To set the value of the key foo to Any? Nil, we need to pass in an external alternative that is not nil, that is, an internal alternative containing a value of nil (Any? Type). In response, we can do the following:

let v: Any? = nil
x["foo"] = v
Copy the code

or

x["foo"] = nil as Any?
Copy the code

That means set it to Any, okay? Nil instead of Any? .

Method 2: updateValue

Alternatively, you can add nil values to the dictionary using the updateValue method:

var dic = [String: Any?]()
dic.updateValue(nil, forKey: "foo")
debugPrint(dic)

// Output: ["foo": nil]
Copy the code

Method 3: NSNull

Can we use NSNull to set nil? Here’s using NSNull and using Any? Code comparison.

Code for using NSNull:

let aKey = "aKey"
var dict = [String: Any?]()
dict[aKey] = NSNull(a)debugPrint(dict)
// Output: ["aKey": Optional(
      
       )]
      
debugPrint(type(of: dict[aKey]))
Swift.Optional
      
       >
      
if dict[aKey] is NSNull {
    debugPrint("value is NSNull")}else {
    debugPrint("value is not NSNull")}// Output: value is NSNull
if let value: Any? = dict[aKey] {
    debugPrint("value is \(value)")
    // Output: value is Optional(
      
       )
      
    if let unwrappedValue = value {
        debugPrint("unwrappedValue is \(unwrappedValue)")}else {
        debugPrint("has no corresponding unwrappedValue")}UnwrappedValue is 
      
}    
Copy the code

Code that uses nil:

let aKey = "aKey"
var dict = [String: Any?] ()let value2: Any? = nil
dict[aKey] = value2
debugPrint(dict)
// output: ["aKey": nil]
debugPrint(type(of: dict[aKey]))
Swift.Optional
      
       >
      

if dict[aKey] is NSNull {
    debugPrint("value is NSNull")}else {
    debugPrint("value is not NSNull")}// Output: value is not NSNull

if let value: Any? = dict[aKey] {
    debugPrint("value is \(value)")
    // Output: value is nil
    if let unwrappedValue = value {
        debugPrint("unwrappedValue is \(unwrappedValue)")}else {
        debugPrint("has no corresponding unwrappedValue")}Has no corresponding unwrappedValue
}
Copy the code

Compare the two pieces of code above, NSNull and Any? It’s not consistent. When using NSNull, the final NSNull object is obtained using Any? , gets nil.

This is not recommended because if you use NSNull, you will also need to treat the existing NSNull object as nonexistent (as if the saved object is nil) when determining whether it has a value.

reference

How to add nil value to Swift Dictionary?