rysv
rysv

Reputation: 3400

Exception 'Invalid type in JSON write (__SwiftValue)' while JSON encoding Swift objects with arrays

Getting exception *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (__SwiftValue)' while trying to encode this Swift object to JSON. All non-optional members, objects Codable. What is the right way to encode or should use some 3rd party library?

struct MediaItem: Codable {
    var key: String = ""
    var filename: String = ""
}

struct NoteTask: Codable {
    var id: String = ""
    var notes: String = ""
    var mediaList: [MediaItem] = []
}

static func addTask(task: NoteTask, callback: @escaping TaskAPICallback) {
    let configuration = URLSessionConfiguration.default
    let session = URLSession(configuration: configuration)

    let url = URL(string: postUrl)
    var request : URLRequest = URLRequest(url: url!)
    request.httpMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    let params: [String: Codable?] = [
        "email": task.id,
        "notes": task.notes,
        "fileList": task.fileList
    ]
    do {
        request.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
    } catch {
        DispatchQueue.main.async {
            callback(false)
        }
        return
    }

    ...
}

Upvotes: 0

Views: 1148

Answers (1)

Andrew Madsen
Andrew Madsen

Reputation: 21373

The issue is that you're using JSONSerialization instead of JSONEncoder. JSONSerialization is the older, Foundation/Objective-C way of writing objects to JSON. It will only work with Foundation objects (see the documentation for a complete list).

Instead, you should use JSONEncoder. The tricky part is that JSONEncoder can't directly encode a Dictionary without some work on your part. There are a few ways to solve this, but if this is the only JSON format you're going to use, I'd probably just create custom keys for your structs using CodingKeys.

struct MediaItem: Codable {
    var key: String = ""
    var filename: String = ""
}

struct NoteTask: Codable {
    var id: String = ""
    var notes: String = ""
    var mediaList: [MediaItem] = []

    enum CodingKeys: String, CodingKey {
        case id = "email"
        case notes = "notes"
        case mediaList = "fileList"
    }
}

static func addTask(task: NoteTask, callback: @escaping TaskAPICallback) {
    let configuration = URLSessionConfiguration.default
    let session = URLSession(configuration: configuration)

    let url = URL(string: postUrl)
    var request : URLRequest = URLRequest(url: url!)
    request.httpMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    do {
        request.httpBody = try JSONEncoder().encode(task)
    } catch {
        DispatchQueue.main.async {
            callback(false)
        }
        return
    }
}

Upvotes: 1

Related Questions