The SwiftyJSON recommended in this issue makes it easy to work with JSON data in Swift.
Why is the typical JSON in Swift not handled well?
Swift is very strict about types. But while explicit typing helps us avoid errors, it can become painful when dealing with JSON and other domains that are inherently implicit in typing.
Take the Twitter API as an example. Suppose we want to retrieve the “name” value of some of a user’s tweets in Swift (according to Twitter’s API).
The code is as follows:
if let statusesArray = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]],
let user = statusesArray[0]["user"] as? [String: Any],
let username = user["name"] as? String {
// Finally we got the username
}
Even if we used an optional chain, it would be messy:
if let JSONObject = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]],
let username = (JSONObject[0]["user"] as? [String: Any])? ["name"] as? String {
// There's our username
}
Looks very messy for something that should be simple.
With SwiftyJSON, all you have to do is:
let json = JSON(data: dataFromNetworking)
if let userName = json[0]["user"]["name"].string {
//Now you got your value
}
Don’t worry about the optional wrapping thing, it will be done for you automatically.
let json = JSON(data: dataFromNetworking)
let result = json[999999]["wrong_key"]["wrong_name"]
if let userName = result.string {
//Calm down, take it easy, the ".string" property still produces the correct Optional String type with safety
} else {
//Print the error
print(result.error)
}
Tool requirements
- iOS 8.0+ | macOS 10.10+ | tvOS 9.0+ | watchOS 2.0+
- Xcode 8
Use
CocoaPods (iOS 8+, OS X 10.9+)
You can install it using CocoaPods by adding it to your Podfile:
platform :ios, '8.0'
use_frameworks!
target 'MyApp' do
pod 'SwiftyJSON', '~> 4.0 '< / span >
end
Carthage (iOS 8+, OS X 10.9+)
You can do this by adding CarthageSwiftyJSON to your Cartfile:
github "SwiftyJSON/SwiftyJSON" ~> 4.0
If you are building dependencies using Carthage, make sure you have added SwiftyJSON.framework to the “Linking Frameworks and libraries” section of the target and included them in your Carthage Framework copy build phase.
Swift Package Manager
You can install using the Swift package Manager by adding the correct description to the file: SwiftyJSONPackage.swift
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "YOUR_PROJECT_NAME",
dependencies: [
.package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "4.0.0"),
]
)
Then swift build when you are ready.
Usage
Initialization
import SwiftyJSON
let json = JSON(data: dataFromNetworking)
or
let json = JSON(jsonObject)
or
if let dataFromString = jsonString.data(using: .utf8, allowLossyConversion: false) {
let json = JSON(data: dataFromString)
}
Subscript
// Getting a double from a JSON Array
let name = json[0].double
// Getting an array of string from a JSON Array
let arrayNames = json["users"].arrayValue.map {$0["name"].stringValue}
// Getting a string from a JSON Dictionary
let name = json["name"].stringValue
// Getting a string using a path to the element
let path: [JSONSubscriptType] = [1,"list",2,"name"]
let name = json[path].string
// Just the same
let name = json[1]["list"][2]["name"].string
// Alternatively
let name = json[1,"list",2,"name"].string
// With a hard way
let name = json[].string
// With a custom way
let keys:[JSONSubscriptType] = [1,"list",2,"name"]
let name = json[keys].string
Error
SwiftyJSON 4.x
SwiftyJSON 4.x introduces an enumeration type called SwiftyJSONError, These include unsupportedType, indexOutOfBounds, elementTooDeep, wrongType,notExist and invalidJSON, while ErrorDomain is
SwiftyJSONError. ErrorDomain. Note: these old error types in SwiftyJSON 4 x has been deprecated, and will be deleted in future versions.
SwiftyJSON 3.x
Use subscripts to get/set values in an array or dictionary
If JSON is:
- An array, the application may crash due to “index out of bounds”.
- A dictionary that will be assigned to it by nil for no reason.
- is not an array or dictionary and the application may crash due to an “unrecognized selector” exception.
This will never happen in SwiftyJSON.
let json = JSON(["name", "age"])
if let name = json[999].string {
// Do something you want
} else {
print(json[999].error!) // "Array[999] is out of bounds"
}
let json = JSON(["name":"Jack", "age": 25])
if let name = json["address"].string {
// Do something you want
} else {
print(json["address"].error!) // "Dictionary["address"] does not exist"
}
let json = JSON(12345)
if let age = json[0].string {
// Do something you want
} else {
print(json[0]) // "Array[0] failure, It is not an array"
print(json[0].error!) // "Array[0] failure, It is not an array"
}
if let name = json["name"].string {
// Do something you want
} else {
print(json["name"]) // "Dictionary[\"name"] failure, It is not an dictionary"
print(json["name"].error!) // "Dictionary[\"name"] failure, It is not an dictionary"
}
Merge
You can merge one JSON into another. Merging one JSON into another adds all the values that don’t exist to the original JSON that only exists in otherJSON.
If two JSons contain values for the same key, the value is usually overwritten in the original JSON, but in both cases it provides some special handling:
- If both values are the values of an array found by json.type.array in otherJSON, they are appended to the array values of the original JSON.
- If both values are one, then json.type.dictionary The two JSON values are merged in the same way that encapsulating JSON is merged.
In cases where two fields in JSON have different types, the value will always be overwritten.
There are two different ways to merge: merge modifies the original JSON, and merged works nondestructively on a copy.
let original: JSON = [
"first_name": "John",
"age": 20,
"skills": ["Coding", "Reading"],
"address": [
"street": "Front St",
"zip": "12345",
]
]
let update: JSON = [
"last_name": "Doe",
"age": 21,
"skills": ["Writing"],
"address": [
"zip": "12342",
"city": "New York City"
]
]
let updated = original.merge(with: update)
// [
// "first_name": "John",
// "last_name": "Doe",
// "age": 21,
// "skills": ["Coding", "Reading", "Writing"],
// "address": [
// "street": "Front St",
// "zip": "12342",
// "city": "New York City"
// ]
// ]
The string indicates
.
There are two options:
- Use one of the default Swift
- Use the custom option “null” that handles options well and represents nil as:
let dict = ["1":2, "2":"two", "3": nil] as [String: Any?]
let json = JSON(dict)
let representation = json.rawString(options: [.castNilToNSNull: true])
// representation is "{\"1\":2,\"2\":\"two\",\"3\":null}", which represents {"1":2,"2":"two","3":null}
—END—
Open Source license: MIT license