7

Overview

There are 2 URLRequests, one with httpBody and one with no httpBody.
However when compared, it shows both are equal.

Question

Is this expected behaviour or am I missing something ?

Code

let url = URL(string: "www.somevalidURL.com")!

var r1 = URLRequest(url: url)
r1.addValue("Content-Type", forHTTPHeaderField: "application/json; charset=utf-8")
r1.httpBody = makeBody(withParameters: ["email" : "a@b.com"])

var r2 = URLRequest(url: url)
r2.addValue("Content-Type", forHTTPHeaderField: "application/json; charset=utf-8")

if r1 == r2 {
    print("requests are equal")
}
else {
    print("requests are not equal")
}

if r1.httpBody == r2.httpBody {
    print("body is equal")
}
else {
    print("body is not equal")
}

func makeBody(withParameters bodyParameters: [String : Any]?) -> Data? {
    guard let bodyParameters = bodyParameters,
        !bodyParameters.isEmpty else {
            return nil
    }
    let body : Data?
    do {
        body = try JSONSerialization.data(withJSONObject: bodyParameters,
                                          options: .prettyPrinted)
    }
    catch {
        print("Error in creating Web Service Body = \(error)")
        body = nil
    }
    return body
}

Output

requests are equal
body is not equal

Xcode 10
Swift Version: 4.2

LinusGeffarth
  • 21,607
  • 24
  • 100
  • 152
user1046037
  • 14,608
  • 12
  • 79
  • 117
  • So `== (lhs:, rhs:)` checks only the equality of the `url` property? Check also with different headers? – Larme Sep 21 '18 at 09:10
  • See updated question, when I compare requests it says it is equal, however when I compare `httpBody` it says it is not equal – user1046037 Sep 21 '18 at 09:12
  • 1
    I'm saying it depends on how Apple implemented the `==` method. If they don't care about the body, but just the url, then they are equals. But what are the criteria to define equal? – Larme Sep 21 '18 at 09:13
  • Fair point, I was using it in some of the test cases, probably will have to manually compare the `httpBody` – user1046037 Sep 21 '18 at 09:19
  • You can use your own `URLRequest` if that's important for you, derivating it, and implementing your own comparing method including the HTTB body, or simply having a custom method `func isCustomEqual(to other: URLRequest) { return self.httpBody == other.httpBody && self == other }`? and use that one. – Larme Sep 21 '18 at 09:22
  • For my requirement, I would just additional comparison for `httpBody` – user1046037 Sep 21 '18 at 09:31

2 Answers2

9

URLRequest is the Swift overlay type for the Foundation type NSURLRequest, so that that == ultimately calls the isEqual() method of the NSURLRequest.

The Foundation library is open source for non-Apple platforms, and at NSURLRequest.swift#L245 we find:

open override func isEqual(_ object: Any?) -> Bool {
    //On macOS this fields do not determine the result:
    //allHTTPHeaderFields
    //timeoutInterval
    //httBody
    //networkServiceType
    //httpShouldUsePipelining
    guard let other = object as? NSURLRequest else { return false }
    return other === self
        || (other.url == self.url
            && other.mainDocumentURL == self.mainDocumentURL
            && other.httpMethod == self.httpMethod
            && other.cachePolicy == self.cachePolicy
            && other.httpBodyStream == self.httpBodyStream
            && other.allowsCellularAccess == self.allowsCellularAccess
            && other.httpShouldHandleCookies == self.httpShouldHandleCookies)

So that seems to be intentional.

Martin R
  • 488,667
  • 78
  • 1,132
  • 1,248
0

If you've come here wondering why your identical URLRequests aren't equal to each other like I did I found the reason to be that URLRequest's equatable implementation differentiates between httpBody being set to nil or being defaulting to nil.

If we do the following:

let request = URLRequest(url: URL(string: "test.com"))
var expectedRequest = URLRequest(url: URL(string: "test.com"))
expectedRequest.httpBody = nil

Then:

request == expectedRequest //false
print(request.httpBody) // nil
request.httpBody = nil
request == expectedRequest //true

Explicitly settings the httpBody to nil will fix this issue. In fact what the body is doesn't matter, only that it has been explicitly set.

request.httpBody = Data()
request == expectedRequest //true

Potential explanation

As Martin rightly points out Swift's source code does not include httpBody. However all of the properties (in the link) being equal (and both being castable as NSURLRequests) does not return true (see lldb output below). I can only presume the linked code is overridden to include another property and this is modified when didSet or willSet is called on httpBody lldb Terminal Log

Declan McKenna
  • 3,803
  • 6
  • 39
  • 67