Converting Data into a Formatted JSON Swift String
This post is brought to you by Emerge Tools, the best way to build on mobile.
Recently I’ve started a large project which involves converting a number of models written in Objective-C over to Swift. As part of my work, I’ve been porting swathes of model definitions that are represented by a flavor of JSON that were used with Pinterest’s excellent Plank library1.
I quickly discovered that there wasn’t a fantastic shortcut to this work, since the definitions we’ve created for our Objective-C codebase aren’t plug-and-play JSON. Here’s an example:
{
"id": "composerLinkImage.json",
"title": "composerLinkImage",
"description" : "Schema definition of Multiple Composer Link Image",
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"height" : {"type" : "integer"},
"width" : {"type" : "integer"},
"type" : {"type" : "string"},
"url" : { "type": "string", "format": "uri"}
}
}
If we want to use some model generation tool to get us 90% of the way there, such as QuickType, this won’t cut it for a few reasons:
- There’s extraneous data I don’t need.
- The only things I do need are keyed to
properties
. - Plus, while it lists the properties and their types, that’s not super awesome because what I actually need are examples of those properties with their values (i.e.
"width" : 200
).
At best, I could copy and paste the properties
and tweak some of it to work:
{
"height" : 0,
"width" : 0,
"type" : "",
"url" : "https://www.test.com"
}
At this point, QuickType will spit out most of what I’m after:
// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
// let welcome = try? JSONDecoder().decode(Welcome.self, from: jsonData)
import Foundation
// MARK: - Welcome
struct ComposerLinkImage: Codable {
let height, width: Int
let type: String
let url: String
}
Of course, this doesn’t scale when I have near 50-60 models to convert over. So, taking stock of the situation, I accepted the fact those pesudo-json definitions weren’t worth much. It was like having a cheat sheet for a test, but all of the answers were written backwards.
But!
What I did have was an internal app I created to hit all of our endpoints to fetch those models:
And therein lied my salvation.
By hitting our own API, I could get grab the raw Data
and then use a little extension to get that into a formatted JSON string. With that, I could get cut out of some of the grunt work by copy and pasting the correctly formatted JSON into a model generation tool. There’d still be tweaks to make, but it does save me a bit of time.
And so it was, I used #ThisOneSimpleTrick it to create a beautiful, copyable and readable JSON string using our good friend Foundation
:
extension Data {
var prettyPrintedJSONString: NSString? {
guard let jsonObject = try? JSONSerialization.jsonObject(with: self, options: []),
let data = try? JSONSerialization.data(withJSONObject: jsonObject,
options: [.prettyPrinted]),
let prettyJSON = NSString(data: data, encoding: String.Encoding.utf8.rawValue) else {
return nil
}
return prettyJSON
}
}
From there, I simply looped through our API calls and used this little helper function to print everything out that I needed:
func dumpRawJSONResponse(for endpoint: URL, params: [String : Any], method: HTTPMethod) {
Task {
let response = await AF.request(endpoint,
method: method,
parameters: params)
.serializingResponse(using: .data).response
guard let responseData = response.data else {
print("There was no data from the response.")
return
}
print(responseData.prettyPrintedJSONString ?? "Couldn't create a .json string.")
}
}
And zing ✨ - there was all of the JSON I needed perfectly formatted. From our previous example, now I had something like this:
{
"height" : 200,
"width" : 200,
"type" : "square",
"url" : "https://www.test.com"
}
And that, as my good friends in the U.K. would say, is a job that’s done and dusted.
Until next time ✌️
-
This tool has saved me several hours, and it’s perfect for generating immutable models for Objective-C codebases. ↩