As the documentation for JSONSerialization
method data(withJSONObject:options:)
says:
If obj
can’t produce valid JSON, JSONSerialization
throws an exception. This exception occurs prior to parsing and represents a programming error, not an internal error. Before calling this method, you should check whether the input can produce valid JSON by using isValidJSONObject(_:)
.
So, it would appear that your parameter, response
, is being supplied something that is not a valid JSON object, insofar as JSONSerialization
can interpret it. Thus it throws an exception that must be resolved during the design/development process, and should not be confused with errors that can be programmatically thrown or caught.
Regards why the exception is occurring, it is becauseJSONSerialization
only allows a narrow selection of types:
- The top level object is an
NSArray
or NSDictionary
…
- All objects are instances of
NSString
, NSNumber
, NSArray
, NSDictionary
, or NSNull
.
- All dictionary keys are instances of
NSString
.
- Numbers are neither NaN or infinity.
You must be using some type that is not understood by JSONSerialization
. Likely candidates include optionals or some custom type. But we cannot be sure which applies in your case without a reproducible example.
So, look at your console to see if the exception provides illuminating information. Do not focus on the hairy looking stack trace, but see if there are any useful diagnostic messages immediately before or after that.
I might also advise that you avoid try?
and use try
instead, wrapping this all in a do
-catch
block. Even if you choose to not return that error for some reason, you can at least print it so you do not lose all useful diagnostic information:
static func parseModel<Response: Decodable>(from response: Any?, with error: Error? = nil) -> Result<Response, Error> {
do {
guard let response else {
return .failure(error ?? APIError.emptyResponse)
}
let data = try JSONSerialization.data(withJSONObject: response)
let model = try JSONDecoder().decode(Response.self, from: data)
return .success(model)
} catch let parseError {
print(parseError)
return .failure(error ?? parseError) // maybe just `.failure(parseError)`
}
}
Theoretically, one could include a test for isValidJSONObject
, but this is generally a programming error that should be remedied, not one that should merely be silenced with a isValidJSONObject
test.
That is the whole reason they consciously made it throw an exception rather merely being an error. There are some edge-cases where you might use isValidJSONObject
(e.g., you are developing a library or service and you want to gracefully inform the caller of the misuse of your function with a custom error), but within our own codebases, it is generally better to avail ourselves of the exception so we can quickly identify and resolve the programming mistake early in the development process.