URL from string with spaces in it

How do I construct a URL from a string with spaces in it?

For example: http://query.yahooapis.com/v1/public/yql?q=select * from yahoo.finance.quote where symbol in ("AAPL")


let text = "http:// + "query.yahooapis.com/v1/public/yql?q=select * from yahoo.finance.quote where symbol in ("AAPL")&format=json&env=http://datatables.org/alltables.env"

let url = NSURL(String: text)

returns url = nil


In related, what must I do re the Apple Transport policy to make this secure?

Just making it https (rather than http) doesn't seem to be sufficient."

Post not yet marked as solved Up vote post of my_o_my Down vote post of my_o_my
15k views

Replies

You should escape the spaces. To do that, you can either change the spaces to "%20" or "+".

  • Nice, but you didn't mention how you'd do that.

Add a Comment

You should look at the NSURLComponents API, which lets you build a URL from its component parts. It will take care of the nightmare that is URL quoting. For example:

import Foundation

let uc = NSURLComponents()
uc.scheme = "http"
uc.host = "query.yahooapis.com"
uc.path = "/v1/public/yql"
uc.queryItems = [
    NSURLQueryItem(name: "q", value: "select * from yahoo.finance.quote where symbol in (\"AAPL\")"),
    NSURLQueryItem(name: "format", value: "json"),
    NSURLQueryItem(name: "env", value: "http://datatables.org/alltables.env")
]
print(uc.URL!.absoluteString)

This prints:

http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.quote%20where%20symbol%20in%20(%22AAPL%22)&format=json&env=http://datatables.org/alltables.env

You can shrink the code by starting with a known, good, no-quoting-required URL string:

import Foundation

let uc = NSURLComponents(string: "http://query.yahooapis.com/v1/public/yql")!
uc.queryItems = … continue as above …

In related, what must I do re the Apple Transport policy to make this secure?

That really depends on the service provider; you’ll have to ask them if they vend the service via HTTPS. If they don’t, accessing the service securely is going to be a challenge. The only reasonable secure option is for you to create your own front end to the service that supports HTTPS. Your app can then talk to your front end securely using HTTPS, which can then talk to the original service via HTTP, making sure to not pass along any identifying information.

The insecure alternatively is to add an ATS exception dictionary to your app.

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>query.yahooapis.com</key>
        <dict>
            <key>NSAppTransportSecurity</key>
            <true/>
        </dict>
    </dict>
</dict>

WARNING This won’t make your networking secure, it’ll just stop ATS complaining about its insecurity.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Is there any improvement on this? I'm getting errors in a Swift script if a filepath uses spaces. (The filepath gets converted to a URL.)


I've got a workaround:


myURL = NSURL.fileURL(withPath: myString)


(Apple's docs say to use fileURLWithPath, but that's deprecated.)


Then I have to use "myURL as URL" to force a type conversion, even though I thought they are supposed to be "toll-free".


IS there a better way?

  • this code gets an error: Initializer for conditional binding must have Optional type, not 'URL'

Add a Comment

IS there a better way?

The droid you’re looking for here is

URL(fileURLWithPath:)
.
let u = URL(fileURLWithPath: "/Users/quinn/Web Staging")
print(u)        // prints "file:///Users/quinn/Web%20Staging/"

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
  • the example you gave above is alright let u = URL(fileURLWithPath: "/Users/quinn/Web Staging")

    But if you wanted to add just a string to a url and this convert the string into url taking all the spaces and making them %20 instead.. the solution above does not work.

    let test = URL(fileURLWithPath: "test this")

    then add this to a url url = URL(string: "https://realty-in-us.p.rapidapi.com/locations/auto-complete?input=\(test)") you want to get "https://realty-in-us.p.rapidapi.com/locations/auto-complete?input=test%20this"

    BUT you get this "https://realty-in-us.p.rapidapi.com/locations/auto-complete?input=test%20this -- file:///"

    how can we remove the "-- file:///" part ?

Add a Comment

I thought I had tried that, and Xcode told me not to use it. It might be possible I'm confusing NSURL with CFURL and URL, of course!


Thanks.

Apple Swift framework developers should be utterly embarrased that this is still happening in 2023. They have obviously been asleep at the wheel for ages now. None of the answers here works.

URL(string: ) should contain allowance to correct for spaces; the fact that it doesn't is truly and utterly embarrassing and costs devs a LOT of f'g wasted time.

  • Same applies for umlauts even if postman show the website supports it nicely ... maybe we should restrict ourselves to 7 bit ascii characters again ...

Add a Comment

Swift String has a var .addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) amongst other options for allowedCharacters which can tidy-up strings used to creat URLs. The docs mention "Calling this method on strings that are already percent-encoded will cause percent characters in a percent-encoded sequence to be percent-encoded twice", but there is also a .removingPercentEncoding var to manage that

I don't know why this old thread has just popped up on "last updated", but I'm going to reply to this part anyway:

URL(string: ) should contain allowance to correct for spaces

No, you can't do that. You need to do appropriate escaping on the components before you construct the string. Once you've constructed the string by concatenating the components, you can't tell if (for example) an = is something that should be escaped to %3D or whether it's the separator between a key and a value that needs to remain as a literal =.