8

Trying to figure out how to prettyPrint the json value of my response from an API, while debugging:

let session = URLSession(configuration: .default)
let task = session.dataTask(with: urlRequest) { data, response, error in
    completion(data, response, error)
}

In the debugger, if I do po data, this is what I get something like this:

enter image description here

How can I print out the actual json structure that is in the data object? Was hoping to see something like this:

{ "firstName" : "John", "lastName" : "Doe", ... }

po debugPrint(data) doesn't output anything in this case.

Prabhu
  • 12,995
  • 33
  • 127
  • 210
  • 4
    @Alexander have you even read the question? It's not at all related to your "duplicate", apart from that it also is related to JSON. One question is about how to output JSON for debugging, the other about an issue with parsing JSON data. _Not a duplicate._ – dr_barto Feb 19 '18 at 22:37
  • @dr_barto `output JSON for debugging` No it's not. OP has an instance of `Data`, which contains jason encoded text, (the bytes shown are UTF8 for `{"mes`), which has not yet been parsed. For OP to get the pretty printed debug output he's looking for, he'll need to parse it first. – Alexander Feb 19 '18 at 23:06
  • 2
    @Alexander yes this is not a duplicate. The linked answer doesn't answer my question on how to get the json when debugging... – Prabhu Feb 20 '18 at 00:26
  • Correct answer is `po String(decoding: response.data!, as: UTF8.self)` – JOM Jul 02 '19 at 09:19
  • Possible duplicate of https://stackoverflow.com/q/38773979/1835803? – Ajith Renjala Oct 04 '19 at 07:56
  • 2
    I found this via google. It's definitely not a duplicate. The issue is being able to inspect the response of an API call in the debugger WITHOUT having to modify the code to do so. Best way to do that is to right-click the local `data` variable in Xcode debugger, and click `Add Expression...` Then you can enter this expression `String(data: data, encoding: .utf8)` and it will create a new string variable in the debugger (and NOT in your source), which lets you read the raw data from the response. – Craig Temple May 18 '20 at 16:56
  • @Alexander-ReinstateMonica It's not possible to define a codable object before you know exactly what JSON you're getting. This question is absolutely valid and different. Please reopen it so that others can view. Thank you. – Matjan Oct 19 '20 at 13:03
  • Looking back, this duplicate is not a good fit. The point stands, however: OP doens't have JSON. He has a `Data`. He can't view the JSON representation of this Data, because he hasn't parsed is as JSON, which is what he needs to do next. It's. Just. Bytes. – Alexander Oct 19 '20 at 14:59
  • @Matjan You can view the string representation of the just just by decoding this `Data` into a `String`, and inspecting that directly. You don't need `JSONSerialization` unless there are truly dynamic keys. – Alexander Oct 19 '20 at 15:00

4 Answers4

10
e print(String(data: JSONSerialization.data(withJSONObject: JSONSerialization.jsonObject(with: data, options: []), options: .prettyPrinted), encoding: .utf8)!)
Ilias Karim
  • 4,798
  • 3
  • 38
  • 60
5

Try the JSONSerialization, something like this:

let url = URL(string: "http://date.jsontest.com")
var request : URLRequest = URLRequest(url: url!)
request.httpMethod = "GET"

let dataTask = URLSession.shared.dataTask(with: request) {
    data,response,error in
    do {
        if let jsonResult = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
            print(jsonResult)
        }
    } catch let error {
        print(error.localizedDescription)
    }
}
dataTask.resume()

Where jsonResult will print out this:

{
    date = "02-19-2018";
    "milliseconds_since_epoch" = 1519078643223;
    time = "10:17:23 PM";
}
Rashwan L
  • 38,237
  • 7
  • 103
  • 107
1
// given raw JSON, return a usable Foundation object
private func convertDataWithCompletionHandler(_ data: Data, completionHandlerForConvertData: (_ result: AnyObject?, _ error: NSError?) -> Void) {
    
    var parsedResult: AnyObject! = nil
    do {
        parsedResult = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as AnyObject
    } catch {
        let userInfo = [NSLocalizedDescriptionKey : "Could not parse the data as JSON: '\(data)'"]
        completionHandlerForConvertData(nil, NSError(domain: "convertDataWithCompletionHandler", code: 1, userInfo: userInfo))
    }
    completionHandlerForConvertData(parsedResult, nil)
}
Ilias Karim
  • 4,798
  • 3
  • 38
  • 60
Jake
  • 2,126
  • 1
  • 10
  • 23
  • `JSONSerialization` is kind of obsolete. You should use codable where possible (like this case). – Alexander Feb 19 '18 at 22:28
  • How do I do this in the debugging console? – Prabhu Feb 19 '18 at 22:41
  • @Prabhu What you have is just `Data`. You could convert it to a string, but the JSON you get isn't necessarily pretty printed, so that won't meet your requirements. To do that, first unwrap your data, then pass it through [`String.init(data:encoding:)`](https://developer.apple.com/documentation/swift/string/1418413-init). You could do it in one shot with `Optional.map`: `po data.map { String(data: $0, encoding: .utf8) }`. – Alexander Feb 19 '18 at 23:14
  • @Prabhu To get actually prettyprinted data, or some other manageable form of this JSON, you'll need to parse it first, like what the duplicate question shows. – Alexander Feb 19 '18 at 23:14
  • @Alexander so there is no command in the debugger that I can use to actually look at the actual json object sent to me by the backend? Any idea why xcode doesn't have this simple feature? Other editors, like Visual Studio for C#, doesn't seem to have an issue showing the json.... – Prabhu Feb 20 '18 at 00:13
  • @Prabhu Because you don't have a "JSOn object" you have bytes. Nothing more. Those bytes happen to use UTF8 to encode JSON, but the IDE can't guess that. – Alexander Feb 20 '18 at 01:37
-1

here's the objective-c code for you.


NSString+PrettyPrint.h

@interface NSString (PrettyPrint)

+ (NSString * _Nonnull)prettifiedJsonStringFromData:(nullable NSData *)data;
+ (NSString * _Nonnull)prettifiedStringFromDictionary:(nullable NSDictionary *)dictionary;

@end

NSString+PrettyPrint.m

#import "NSString+PrettyPrint.h"

@implementation NSString (Prettified)

- (NSString *)trimForCount:(NSUInteger)count {
    return self.length > count ? [self substringWithRange:NSMakeRange(0, count)] : self;
}

+ (NSString *)prettifiedStringFromDictionary:(nullable NSDictionary *)dictionary {
    
    if (dictionary == nil) { return @"nil"; }
    
    NSMutableString *returnStr = [NSMutableString stringWithString:@"[ \n"];
    
    for (NSString *key in dictionary) {
        [returnStr appendFormat:@"  %@: %@,\n", key, [dictionary valueForKey:key]];
    }
    
    [returnStr appendFormat:@"]"];
    
    return returnStr;
}

+ (NSString *)prettifiedJsonStringFromData:(nullable NSData *)data {
    
    if (data == nil) { return @"nil"; }
    
    
    NSData *jsonData;
    NSError *error = nil;
    
    NSString *jsonStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    jsonData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
    id jsonObject;
    @try {
        jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:&error];
    } @catch(id anException) {
        return anException ? [NSString stringWithFormat:@"Json Parse Exception: %@", anException] : jsonStr ? jsonStr : @"json parse exception";
    }
    if (jsonObject == nil) {
        return jsonStr ? jsonStr : @"nil (json object from data)";
    } else {
        BOOL isValidJsonObject = [NSJSONSerialization isValidJSONObject:jsonObject];
        if (isValidJsonObject) {
            NSData *finalData = [NSJSONSerialization dataWithJSONObject:jsonObject options:NSJSONWritingPrettyPrinted error:&error];
            //TODO: error description
            NSString *prettyJson = [[NSString alloc] initWithData:finalData encoding:NSUTF8StringEncoding];
            return prettyJson;
        } else {
            return [NSString stringWithFormat:@"%@\n%@", jsonStr, @" (⚠️ Invalid json object ⚠️)\n"];
        }
    }
}

@end

then call the methods,

ex1. Print NSData for body, response ...etc

NSLog(@"body: %@", [NSString prettifiedJsonStringFromData:[request HTTPBody]]);

ex2. Print NSDictionary

NSLog(@"headers: %@", [NSString prettifiedStringFromDictionary:[request allHTTPHeaderFields]]);

Probably you'll get these results in log.

enter image description here

enter image description here

edit log:

edit at 220112, preventing from exception

Bomi Chen
  • 81
  • 4