Search code examples
iosswifturlnsurl

How to parse URL with # in Swift?


Suppose, I have following URL: https://something.com/room/order/12345555/product/543333?is_correct=true. It is kind of deeplink and I should parse its parameters and show some ViewController. I am interested in values as 12345555, 543333 and true. Actually, it is easy to get those parameters.

In order to get 12345555 or 543333, we can use pathComponents of URL which returns ["/", "room", "order", "12345555", "product", "543333"]. To get query items (is_correct: true), we can use URLComponents. Everything is clear and simple.

But suppose my link contains # as path https://something.com/room/#/order/12345555/product/543333?is_correct=true. Now, for this link, pathComponents returns just ["/", "room"] ignoring everything else. Of course, there are also problems with query parameters.

Why does # symbol affect so? How can I solve problem? Should I just replace # with something or URL from Swift contains some helper methods? Thanks.


Solution

  • The problem you're running into is that # isn't part of the path but introducing a new component of the URL, stored in url.fragment. It's similar to if you had https://example.com/foo/?test=/bar. ?test= isn't a path component but the beginning of the query.

    You have two approaches you can take.

    If https://something.com/room/order/12345555/product/543333?is_correct=true and https://something.com/room/#/order/12345555/product/543333?is_correct=true can be used interchangeably, as in viewing either page in the browser will land you on the same page, you could have a sanitizing step in your process:

    var rawUrl = ...
    var sanitizedUrl = url.replacingOccurrences(of: "/#/", with: "/")
    var url = URL(string: url)
    

    How much sanitization you do depends on your application. It could be that you only want to do (of: "/room/#/", with: "/room/")

    Another option, if you know your fragment will always look like a partial URL would be to pass the fragment into URL:

    let url = URL(string: rawUrl)!
    let fragmentUrl = URL(string: url.fragment!, relativeTo: url)!
    
    let fullPathComponents = url.pathComponents + fragmentUrl.pathComponents[1...];
    var query = fragmentUrl.query
    

    The above approach yields: ["/", "room", "order", "12345555", "product", "543333"] for the joined URL.

    Which approach and how much sanitization you do will depend on your use-case.