14

Under IOS, I wish to identify pairs of NSURLs that are either identical or differ slightly but refer to the same destination. For instance http://google.com vs http://google.com/.

isEquals reports those NSURLs as diferent. Nor can I find a way to obtain a 'canonical' or 'normalized' form of an NSURL to compare those forms instead.

NSURL *a = [NSURL URLWithString:@"http://google.com"];
NSURL *b = [NSURL URLWithString:@"http://google.com/"];

if([a isEqual:b]) {
  NSLog(@"Same"); // Not run
}

if([[a absoluteURL] isEqual:[b absoluteURL]]) {
  NSLog(@"Same"); // Still not run
}

if([[a URLByStandardizingPath] isEqual:[b URLByStandardizingPath]]) {
  NSLog(@"Same"); // Still not run
}

How can I compare two NSURLs while ignoring string differences that do not affect the intent of the URL?

Jim Blackler
  • 22,946
  • 12
  • 85
  • 101
  • what is your goal here? does it only apply to trailing slashes, does it apply to parameters as well? files? – Jake Apr 27 '11 at 09:02
  • Not parameters as that would affect the intent of the URL. – Jim Blackler Apr 27 '11 at 09:06
  • The motivation is that `UIWebViewDeglate`'s `webView:shouldStartLoadWithRequest..` can specify URLs in a different form to the form sent to `UIWebView:loadRequest`. – Jim Blackler Apr 27 '11 at 09:10
  • I’ve seen Web sites where `http://host/somepath` and `http://host/somepath/` yield different results so I don’t think there’s a general solution for your problem. –  Apr 27 '11 at 09:16
  • And `-URLByStandardizingPath` only works on file system URLs. –  Apr 27 '11 at 09:16
  • @Bavarious: `http://host/path/` and `http://host/` are not analogous. RFC2616 explicitly states that an empty path component is equivalent to `/` but other than that a path with a `/` on the end is not the same as a path without the `/`. – JeremyP Apr 27 '11 at 09:47
  • How about just taking the host component, maybe some other components of the URL, and base a comparison on that? – Jake Apr 27 '11 at 09:57
  • @JeremyP I see. So the only situation where the OP can locally test for equivalency is the path being an empty string or `/`. –  Apr 27 '11 at 10:02
  • @Bavarious: Yes, or if the two paths are identical (including case) after normalisation (removing `../` and `./` and converting percent encodings for non reserved characters to plain text). – JeremyP Apr 27 '11 at 10:09
  • Just a note that by convention "isEqual:" test if two references refer to the same object, not whether the value the objects themselves hold can be considered logically equal. Equality of value is very tricky when it comes different types of data e.g. Are two date objects equal if they are set to different hours of the same day? The usual test for this is comparison. – TechZen Aug 15 '13 at 21:10

1 Answers1

20

According to RFC 2616, http://google.com and http://google.com/ are equivalent. This is what it says (section 3.2.3):

When comparing two URIs to decide if they match or not, a client SHOULD use a case-sensitive octet-by-octet comparison of the entire URIs, with these exceptions:

  • A port that is empty or not given is equivalent to the default port for that URI-reference;

  • Comparisons of host names MUST be case-insensitive;

  • Comparisons of scheme names MUST be case-insensitive;

  • An empty abs_path is equivalent to an abs_path of "/".

For reference, the syntax is given as http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]

Unfortunately, NSURL has to support schemes other than HTTP, so it cannot make the assumption that RFC 2616 provides a generic rule. I think your best fix is to create a category with your own compare method for http URLs that specifically checks for an empty absolute path.

Community
  • 1
  • 1
JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • While JeremyP is completely correct, it is reasonable to expect that either NSURL (or NSURLRequest) would be able to implement RFC 2616 for applicable URLs. It is not ambiguous when RFC 2616 applies. If this impacts you, I would recommend opening a radar at bugreport.apple.com. – Rob Napier Sep 07 '12 at 02:29