How to parse URL with # in Swift? - ios

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.

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.

Related

Swift URL Session Response gets corrupted when using '&' symbol

I am developing iOS app, I am using URL Session method for make API calls. Api response gives one property which contains AWS s3 link for pdf document.
What should happen:
So in this app, I am retrieving a PDF document from the server to view in the app, so in the GET URL that I'm sending will give a response of an access link of the pdf document generated through AWS S3.
What is happening now:
Document name: Document/sdd3343-sfnf0asdnd0UserB&ServiceLetter.pdf (notice there is an '&' sign)
In the android and Swagger application, this GET URL is working perfect on any circumstances.
but in the iOS version- in the URL session, when ever when there is a '&' sign inside the document's name, the responding access link gets corrupted.
Now in Android and Swagger, when we are accessing the same document, it works perfectly, but for iOS it doesn't.
URL that doesn't work on iOS but that works in Android and Swagger:
https://domainName/api/FileUpload/GetDocumentUrl?S3Key=**Document/sdd3343-sfnf0asdnd0UserB&ServiceLetter.pdf**&fileCategory=2&userId=9888900000
Above url having parameter name called 'S3Key' value is Document/sdd3343 sfnf0asdnd0UserB&ServiceLetter.pdf and it having an ampersand '&' symbol in middle of the name.
iOS response for the above URL:
{"success":true,"response":"https://samplesite.s3.ap-southwest-2.amazonaws.com/Document/sdd3343-sfnf0asdnd0UserB?X-Amz-Expires=3600&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAWEWNDEFIENS88NKWSWIULHKA/20221010/ap-southwest-2/s3/aws4_request&X-Amz-Date=20221010T011135Z&X-Amz-SignedHeaders=host&X-Amz-Signature=44cab95333c0b5e385959835948539845948023823483","error":null}
In here you can see the response which got by the URL Session. But the reponse url body cut off from ampersand symbol.
Current Response: /Document/sdd3343-sfnf0asdnd0UserB?X-Amz-Expires=3600&X.........
Expected Response: /Document/sdd3343-sfnf0asdnd0UserB&ServiceLetter.pdf?X-Amz-Expires=3600&X.........
How to solve this URL Session problem
Note how & characters are used to separate key/value pairs in a URL. Because of this, you cannot have an & in the middle of a value within a URL, because it is interpreted as a delimiter before the next key/value pair. The answer is to percent-escape the & in the value associated with the S3Key key. To do this, the easiest way is URLComponents:
guard var components = URLComponents(string: "https://domainName/api/FileUpload/GetDocumentUrl") else {
print("URLComponents failure")
return
}
components.queryItems = [
URLQueryItem(name: "S3Key", value: "Document/sdd3343-sfnf0asdnd0UserB&ServiceLetter.pdf"),
URLQueryItem(name: "fileCategory", value: "2"),
URLQueryItem(name: "userId", value: "9888900000")
]
guard let url = components.url else {
print("unable to build url")
return
}
print(url) // https://domainName/api/FileUpload/GetDocumentUrl?S3Key=Document/sdd3343-sfnf0asdnd0UserB%26ServiceLetter.pdf&fileCategory=2&userId=9888900000
There are other ways to manually percent-escape the values in the URL, but URLComponents does it reasonably gracefully.

How to get the redirect url from deeplink in Swift

How to get the URL from the given redirect deeplink string?
For example, if I have a deeplink string as
myapp://open-browser/https://mydaily.dev/JOURNAL/home?id=123e4567-e89b-12d3-a456-426655440000&source=01&channel_id=HOME
I would like to get the return result as
https://mydaily.dev/JOURNAL/home?id=123e4567-e89b-12d3-a456-426655440000&source=01&channel_id=HOME
I tried several methods from Apple documentation
Accessing the Parts of a URL,
the closest one was to combine path and query like below screenshot
Does anyone have a better way to do this?
I get your question, I think you want to get only the url starting from https://, please correct me.
If so I think I could remove the deeplink url scheme myapp:// and the host open-browser/ like
let url = "myapp://open-browser/https://mydaily.dev/JOURNAL/home?id=123e4567-e89b-12d3-a456-426655440000&source=01&channel_id=HOME"
let deeplinkBase = "myapp://open-browser/"
let newUrl = url.absoluteString.replacingOccurrences(of: deeplinkBase, with: "")
Then you could use the newUrl as you like since we got the expected result now
https://mydaily.dev/JOURNAL/home?id=123e4567-e89b-12d3-a456-426655440000&source=01&channel_id=HOME
My example for the deeplinkBase string is hardcoded, but I guess you could use enums or any handlers for it later.

Create URL with /#/ in path and open in safari

I use firebase dynamic links which contain an URL to our webapp.
If the dynamic link is opened, the deep link is fetched.
So far so good. As we use the /#/path pattern in our webapp to redirect a user to different sections, we have a problem now, creating such an url in our iOS application after we have to append a new parameter in the url
If this example URL is in our dynamic link
https://domain/#/main/page?utm_source=app&utm_medium=button&utm_campaign=testcampaign
i get it and have to append a parameter for autologin mechanism in our webapp.
So here is the point where i fail at two different approaches.
Getting the string from the url and appending the token parameter and value.
This approach works fine until I have to parse the urlString back to an URL object. The /#/ inside causes an error when creating a new URL object.
I try to replace /#/ with /%23/ (encoded #), but this does not work on our ngnix / webapp infrastructure.
Appending the token parameter with new URLQueryItem in URLComponents.
This approach leads to a wrong URL resulting in (token is the added parameter)
https://my-stage.bikersos.com/?token=tokrenvalue#/main/premium?utm_source=app&utm_medium=button&utm_campaign=testcampaign
I append the URL Query Item with this extension
extension URL {
func addQueryParams(newParams: [URLQueryItem]) -> URL? {
let urlComponents = NSURLComponents.init(url: self, resolvingAgainstBaseURL: false)
guard urlComponents != nil else { return nil; }
if (urlComponents?.queryItems == nil) {
urlComponents!.queryItems = []
}
urlComponents!.queryItems!.append(contentsOf: newParams)
print(urlComponents!)
return urlComponents?.url
}
}
does anybody has an idea how I could solve this problem? I personally prefer the second approach, if it is possible to append the parameters at the end
I figured it out how it has to be done with firebase and utm parameters (this link can be added as deep link in a dynamic link for firebase)
https://example.domain.com/?utm_source=newsletter&utm_medium=button&utm_campaign=testcampaign#/path1/subpath?webappparam1=1&webappparam2=asdf
This way all utm parameters are applied and the path will be available in the web application too.
You can add new query params using the iOS SDK but be aware, they are added at the utm parameters location.
If you need to add them at the end, check if there already exists an ? in the path and write your own appending at the end of the url.

Check the validity of a domain name

I need to make sure that a URL that the user inputs goes where it should go. I mean if the user inputs "http://google.com/blablabla" or "http://google123.com/blablabla". I need to figure out that the second url is not correct because it won't open google's web-site. Is there some method in Swift to do that? Or do I just need to check if the URL contains "http://google.com"?
1. Malformed URL
To verify whether or not the URL itself is malformed, let the OS do it:
(check that the URL has all the parts it needs, namely a scheme and a host)
if let url = URL(string: rawStringInput),
let _ = url.scheme,
let _ = url.host {
// rawStringInput is well formed
}
You can then query all sorts of information from the URL, such as scheme (http), the host (google.com), the path (blablabla), fragment, port and so on.
2. Valid URL
If you want to know whether or not the URL is reachable (i.e. the remote server responds to it), you actually need to execute the request.

Replace.string with a URL as parameter

Below I have this code:
string _strTemplate = _strDownloadTemplate + IDReq + "/" + _strFileName;
Uri url = new Uri(_strTemplate);
As you can see, I'm converting the strTemplate (which carries the link of a page that I need to sent by email for the user) to a URL Format. My email body has several fields that I'm replacing with the correct value:
strMailMessage = strMailMessage.Replace("_LinkTemplate", url);
I'm getting an error because the method string.Replace takes strings as parameters only.
Is there a way to get around this?
I was thinking about pass the URL value through my page (page.aspx) but if there's a way to do so through this method, it would be better for me.
Thanks!
Assuming this is C# and .NET, yes, String.Replace() works with strings.
Did you try:
strMailMessage = strMailMessage.Replace("_LinkTemplate", url.ToString());

Resources