Skip to content

Instantly share code, notes, and snippets.

@robertmryan
Last active November 14, 2019 02:02
Show Gist options
  • Save robertmryan/03542e2da539ad7f51c2540bafdad97e to your computer and use it in GitHub Desktop.
Save robertmryan/03542e2da539ad7f51c2540bafdad97e to your computer and use it in GitHub Desktop.
struct Cost: Codable {
let id: String
let label: String
let value: String
}
let cost = Cost(id: "something", label: "something", value: "something")
let jsonData = try! JSONEncoder().encode(cost) // this works fine
let object = try! JSONSerialization.jsonObject(with: jsonData) // this works fine
@robertmryan
Copy link
Author

The backslashes are perennial source of confusion. Let’s say you have a text string this is a "test" string. Depending upon how you display this (e.g. if you look at in the debugger, it may show it to you as "this is a \"test\" string". If you see the whole thing in quotes, then quotation marks inside that quoted string are shown with \ escape character, even though that \ isn’t really in the string.

And when you create your own string literals, you have to escape the " characters with \ (though, again, the \ character doesn’t actually appear in the string ... it’s only there for escaping):

let string = "This is a \"test\" string”

As Swift has evolved, they’ve come up with two ways to eliminate this need for escaping the quotation marks within a string. You can use the #" and "# pattern, e.g.

let string = #"this is a "test" string"#

Thus:

let string = #"{"billing":{"phone":"۰۹۳۵۴۰۰۹۷۴۰","city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","last_name":"مقیمی","company":"","postcode":"۱۲۳۴۵۶۷۸۹","email":"","address_2":"","state":"THR","first_name":"محسن"},"set_paid":false,"line_items":[{"subtotal":"20000","product_id":158243,"quantity":1},{"subtotal":"17500","product_id":158236,"quantity":1}],"payment_method_title":"بانک سامان","shipping_lines":[{"instance_id":4,"id":4,"title":"پست سفارشی","method_title":"نرخ ثابت","settings":{"cost":{"id":"cost","label":"هزینه","value":"8900"}},"method_id":"flat_rate"}],"coupon_lines":[],"customer_id":8360,"payment_method":"WC_Saman_Gateway","shipping":{"city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","postcode":"۱۲۳۴۵۶۷۸۹","last_name":"مقیمی","address_2":"","state":"THR","first_name":"محسن"}}"#

Or you can use the """ convention (which not only eliminates this need for escaping, but also allows you to write multi line strings):

let string = """
    this is a "test" string
    """

Or

let string = """
    {"billing":{"phone":"۰۹۳۵۴۰۰۹۷۴۰","city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","last_name":"مقیمی","company":"","postcode":"۱۲۳۴۵۶۷۸۹","email":"","address_2":"","state":"THR","first_name":"محسن"},"set_paid":false,"line_items":[{"subtotal":"20000","product_id":158243,"quantity":1},{"subtotal":"17500","product_id":158236,"quantity":1}],"payment_method_title":"بانک سامان","shipping_lines":[{"instance_id":4,"id":4,"title":"پست سفارشی","method_title":"نرخ ثابت","settings":{"cost":{"id":"cost","label":"هزینه","value":"8900"}},"method_id":"flat_rate"}],"coupon_lines":[],"customer_id":8360,"payment_method":"WC_Saman_Gateway","shipping":{"city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","postcode":"۱۲۳۴۵۶۷۸۹","last_name":"مقیمی","address_2":"","state":"THR","first_name":"محسن"}}"#
    """

For more information, see The Swift Programming Language: String Literals.

@MohsenMoghimi
Copy link

MohsenMoghimi commented Nov 9, 2019

let order = NewOrder(billing: billing, couponLine: [], customerId: customerId, lineItems: lineItems, paymentMethod: paymentMethod, paymentMethodTitle: paymentMethodTitle, setPaid: false, shipping: shipping, shippingLines: shippingLine)
let jsonData = try! JSONEncoder().encode(order)
print(JSONSerialization.isValidJSONObject(jsonData))

i was wrong, isValidJSONObject still prints false

@robertmryan
Copy link
Author

robertmryan commented Nov 9, 2019

It’s either:

let foo = "some \"quoted\" string"

or you can use extended string delimiters with #" and "#:

let foo = #"some "quoted" string"#

or you can use the multiline string literal, with """ (note, three, not two "):

let foo = """
    some "quoted" string
    """

If you want to do string interpolation, it’s either:

let bar = "the value is \(foo)"

Or within the extended string delimiters, you can use \#(foo) for interpolation:

let bar = #"the value is \#(foo)"#

Or you can use the multiline string literal with the standard string interpolation syntax:

let bar = """
    the value is \(foo)
    """

@robertmryan
Copy link
Author

robertmryan commented Nov 9, 2019

let order = NewOrder(billing: billing, couponLine: [], customerId: customerId, lineItems: lineItems, paymentMethod: paymentMethod, paymentMethodTitle: paymentMethodTitle, setPaid: false, shipping: shipping, shippingLines: shippingLine)
let jsonData = try! JSONEncoder().encode(order)
print(JSONSerialization.isValidJSONObject(jsonData))

i was wrong, isValidJSONObject still prints false

You misunderstand the purpose of isValidJSONObject. This method is just telling you whether you can take the object and pass it to JSONSerialization.data(withJSONObject:) without failing. Unlike JSONEncoder, JSONSerialization is extremely limited in terms of what it can encode; see “Overview” section of JSONSerialization documentation for specifics.

But you pass it an object to be encoded, not the Data that is the raw JSON:

let foo = ["a": 1]
print(JSONSerialization.isValidJSONObject(foo))  // true

let bar = ["a", "b", "c"]
print(JSONSerialization.isValidJSONObject(bar))  // true

let baz = 42
print(JSONSerialization.isValidJSONObject(baz))  // false because it has to be array or dictionary

let qux = [NSObject()]
print(JSONSerialization.isValidJSONObject(qux))  // false because the object in the array cannot be represented

@robertmryan
Copy link
Author

If you want to see if the Data is valid JSON, you’ll have to actually attempt the decode...

@MohsenMoghimi
Copy link

the problem solved, it was just a type mismatch, one of the numeric parameters most be string but I send it in Integer.
thanks a lot for your helps my friend, I so sorry for wasting your time.

@robertmryan
Copy link
Author

👍

@MohsenMoghimi
Copy link

hi again my friend, I'm have issue with my network layer, would you please help me???

@robertmryan
Copy link
Author

Go ahead an add your question here. Worst case scenario, if it’s too complicated, I might suggest that you post it as a proper question on StackOverflow, as I’m buried right now.

@MohsenMoghimi
Copy link

I really don't know about it complication, I will ask and you take decision that its complicated or not.

@MohsenMoghimi
Copy link

MohsenMoghimi commented Nov 13, 2019

I have APIClient in my project and this is the code:

protocol APIClientType {
    func request<M: Codable>(_ endpoint: URLRequestConvertible) -> Single<M>
}

final class NewAPIClient: NSObject, URLSessionDelegate, APIClientType {
    
    var session: URLSession!
    
    override init() {
        super.init()
        
        let urlsession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue.main)
        urlsession.configuration.allowsCellularAccess = true
        urlsession.configuration.timeoutIntervalForRequest = TimeInterval(10.0)
        
        self.session = urlsession
    }
    
    func request<M: Codable>(_ endpoint: URLRequestConvertible) -> Single<M> {
        return Single<M>.create { [unowned self] single in

            let request = endpoint.request()

            #if DEBUG
            print("REQUEST: \(debugPrint(request))")
            #endif
            
            let task = self.session.dataTask(with: request) { (data, response, error) in
                if let responseError = error {
                    single(.error(responseError))
                }
                
                #if DEBUG
                print("RESPONSE: \(debugPrint(response ?? "NULL"))")
                #endif
                
                if let httpResponse = response as? HTTPURLResponse {
                    switch httpResponse.statusCode {
                    case 200, 201:
                        guard let data = data else {
                            break
                        }
                        
                        do {
                            let model = try JSONDecoder().decode(M.self, from: data)
                            single(.success(model))
                        } catch let myJSONError {
                            single(.error(myJSONError))
                        }
                        
                    case 404:
                        single(.error(APIError.NotFound))
                        
                    default:
                        break
                    }
                }
            }
            
            task.resume()
            
            return Disposables.create {
                task.cancel()
            }
        }
    }
}

@MohsenMoghimi
Copy link

MohsenMoghimi commented Nov 13, 2019

in 2 case of web services the response is simple text not a json, something like "the task done", my issue is when I intent to get this responses the " request " function returns myJSONError, how can I modify this class to return a simple string response?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment