Json in Swift

import Foundation

// We'll use these two structs to demonstrate encoding and
// decoding of custom types below.
struct Response1: Codable {
    let page: Int
    let fruits: [String]
}

// Only properties will be encoded/decoded in JSON.
// You can use CodingKeys to customize the encoded JSON key names.
struct Response2: Codable {
    let page: Int
    let fruits: [String]
    
    enum CodingKeys: String, CodingKey {
        case page
        case fruits
    }
}

// First we'll look at encoding basic data types to
// JSON strings. Here are some examples for atomic values.
let boolValue = true
let boolJSON = try! JSONEncoder().encode(boolValue)
print(String(data: boolJSON, encoding: .utf8)!)

let intValue = 1
let intJSON = try! JSONEncoder().encode(intValue)
print(String(data: intJSON, encoding: .utf8)!)

let floatValue = 2.34
let floatJSON = try! JSONEncoder().encode(floatValue)
print(String(data: floatJSON, encoding: .utf8)!)

let stringValue = "gopher"
let stringJSON = try! JSONEncoder().encode(stringValue)
print(String(data: stringJSON, encoding: .utf8)!)

// And here are some for arrays and dictionaries, which encode
// to JSON arrays and objects as you'd expect.
let sliceValue = ["apple", "peach", "pear"]
let sliceJSON = try! JSONEncoder().encode(sliceValue)
print(String(data: sliceJSON, encoding: .utf8)!)

let mapValue = ["apple": 5, "lettuce": 7]
let mapJSON = try! JSONEncoder().encode(mapValue)
print(String(data: mapJSON, encoding: .utf8)!)

// The JSONEncoder can automatically encode your
// custom data types.
let res1 = Response1(page: 1, fruits: ["apple", "peach", "pear"])
let res1JSON = try! JSONEncoder().encode(res1)
print(String(data: res1JSON, encoding: .utf8)!)

let res2 = Response2(page: 1, fruits: ["apple", "peach", "pear"])
let res2JSON = try! JSONEncoder().encode(res2)
print(String(data: res2JSON, encoding: .utf8)!)

// Now let's look at decoding JSON data into Swift
// values. Here's an example for a generic data structure.
let jsonString = "{\"num\":6.13,\"strs\":[\"a\",\"b\"]}"
let jsonData = jsonString.data(using: .utf8)!

// We need to provide a type where the JSON
// package can put the decoded data. This
// [String: Any] will hold a dictionary of strings
// to arbitrary data types.
let jsonObject = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any]
print(jsonObject)

// In order to use the values in the decoded dictionary,
// we'll need to cast them to their appropriate type.
// For example here we cast the value in `num` to
// the expected `Double` type.
let num = jsonObject["num"] as! Double
print(num)

// Accessing nested data requires a series of
// casts.
let strs = jsonObject["strs"] as! [String]
let str1 = strs[0]
print(str1)

// We can also decode JSON into custom data types.
// This has the advantages of adding additional
// type-safety to our programs and eliminating the
// need for type casts when accessing the decoded data.
let jsonString2 = "{\"page\": 1, \"fruits\": [\"apple\", \"peach\"]}"
let jsonData2 = jsonString2.data(using: .utf8)!
let res = try! JSONDecoder().decode(Response2.self, from: jsonData2)
print(res)
print(res.fruits[0])

// In the examples above we always used Data and
// strings as intermediates between the data and
// JSON representation. We can also stream JSON
// encodings directly to OutputStream or even network sockets.
let jsonEncoder = JSONEncoder()
let outputStream = OutputStream.toMemory()
outputStream.open()
let dictionary = ["apple": 5, "lettuce": 7]
try! jsonEncoder.encode(dictionary, to: outputStream)
outputStream.close()

if let data = outputStream.property(forKey: .dataWrittenToMemoryStreamKey) as? Data,
   let string = String(data: data, encoding: .utf8) {
    print(string)
}

This Swift code demonstrates JSON encoding and decoding, including working with custom types, handling nested data, and streaming JSON output. The structure and explanations have been maintained from the original example, adapted to Swift’s syntax and conventions.

Swift’s Codable protocol provides a convenient way to encode and decode custom types, which is similar to but more straightforward than Go’s approach. The JSONEncoder and JSONDecoder classes are used for encoding and decoding, respectively.

Note that Swift uses optionals and throws errors in many cases where Go would return multiple values including an error. In this example, we’ve used force unwrapping and try! for simplicity, but in production code, you’d want to handle these potential errors more gracefully.