guykogus/swifterjson
**JSON in Swift — the way it should be.**
Features
- Type-safe enum — every JSON value is represented as a
JSONcase (null,bool,int,double,string,array,object) - Codable — decode from
Data/Stringvia standardJSONDecoder; encode back viaJSONEncoder - Subscript access — chain
[key]and[index]subscripts to drill into nested structures - Mutable — modify deeply nested values in-place with subscript assignment
- Literal expressible — create
JSONvalues directly from Swift literals (strings, numbers, arrays, dictionaries,nil) - Codable interop — convert between
JSONand anyCodabletype without round-tripping throughData - Raw value bridging — convert to/from
Anyfor interop with APIs that use untyped dictionaries - Sendable & Hashable — safe for concurrent use and usable as dictionary keys
- Cross-platform — iOS, macOS, tvOS, watchOS, visionOS, Linux, Android, and WebAssembly
Requirements
| Dependency | Minimum | |---|---| | Swift | 6.0+ | | iOS | 15.0+ | | macOS | 11.0+ | | tvOS | 15.0+ | | watchOS | 8.0+ | | visionOS | 1.0+ | | Linux | Swift 6.0 toolchain | | Android | Swift 6.0 toolchain | | WebAssembly | SwiftWasm 6.1 SDK |
Installation
Swift Package Manager
Add SwifterJSON as a dependency in your Package.swift:
dependencies: [
.package(url: "https://github.com/guykogus/SwifterJSON.git", from: "4.0.0")
]Then add it to your target:
.target(
name: "YourTarget",
dependencies: ["SwifterJSON"]
)Or in Xcode: File > Add Package Dependencies… and enter https://github.com/guykogus/SwifterJSON.git.
Usage
The problem
In the modern era of Codable it is rare that we need to handle JSON data manually. But sometimes the structure isn't known in advance — server-driven UI, feature flags, analytics payloads, etc.
Given this JSON:
{
"Apple": {
"address": {
"street": "1 Infinite Loop",
"city": "Cupertino",
"state": "CA",
"zip": "95014"
},
"employees": 132000
}
}With JSONSerialization you'd have to cast at every level:
guard let companies = try JSONSerialization.jsonObject(with: data) as? [String: Any] else { return }
if let company = companies["Apple"] as? [String: Any],
let address = company["address"] as? [String: Any],
let city = address["city"] as? String {
print("Apple is in \(city)")
}Mutations are even worse — you need mutable copies at each nesting level:
guard var companies = try JSONSerialization.jsonObject(with: data) as? [String: Any] else { return }
if var apple = companies["Apple"] as? [String: Any],
var address = apple["address"] as? [String: Any] {
address["state"] = "California"
apple["address"] = address
companies["Apple"] = apple
}With SwifterJSON
Reading nested values is a single chained expression:
let companies = try JSONDecoder().decode(JSON.self, from: data)
if let city = companies["Apple"]?["address"]?["city"]?.stringValue {
print("Apple is in \(city)")
}Mutations work in-place — no intermediate copies:
var companies = try JSONDecoder().decode(JSON.self, from: data)
companies["Apple"]?["address"]?["state"] = "California"Constructing JSON from literals
JSON conforms to all the ExpressibleBy…Literal protocols, so you can write JSON values naturally:
let config: JSON = [
"feature_flags": [
"dark_mode": true,
"max_retries": 3,
"api_url": "https://api.example.com"
],
"version": 2.1
]Converting between JSON and Codable types
Encode any Encodable value into JSON without going through Data:
struct User: Codable {
let name: String
let age: Int
}
let user = User(name: "Alice", age: 30)
let json = try JSON(encodableValue: user)
// json == ["name": "Alice", "age": 30]Decode JSON back into a typed model:
let decoded: User = try json.decode()Accessing values
Each JSON type has a corresponding accessor that returns an optional:
json.boolValue // Bool?
json.intValue // Int?
json.doubleValue // Double?
json.stringValue // String?
json.arrayValue // [JSON]?
json.objectValue // [String: JSON]?
json.isNull // Bool
json.count // Int? (for arrays and objects)Array subscripting
let fibonacci: JSON = [1, 1, 2, 3, 5, 8, 13]
fibonacci[4]?.intValue // 5Raw value interop
Bridge to/from Any for APIs that use untyped dictionaries:
let raw: Any? = json.rawValue
let roundTripped = JSON(rawValue: raw)License
SwifterJSON is available under the MIT license. See the LICENSE file for details.
Package Metadata
Repository: guykogus/swifterjson
Default branch: main
README: README.md