4

Converting Data into a Formatted JSON Swift String

 1 year ago
source link: https://www.swiftjectivec.com/dumping-api-data-as-json-string-in-swift/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Converting Data into a Formatted JSON Swift String

// Written by Jordan Morgan // Feb 2nd, 2023 // Read it in about 2 minutes // RE: Swift

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:

  1. There’s extraneous data I don’t need.
  2. The only things I do need are keyed to properties.
  3. 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.

What I did have was an internal app I created to hit all of our endpoints to fetch those models:

Screenshots of an internal iOS running that shows APIs and their responses.

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 ✌️

  1. This tool has saved me several hours, and it’s perfect for generating immutable models for Objective-C codebases. 


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK