NSURL errors with caret ^ in string

2019-09-10 15:21发布

问题:

Attempting to get data on the S&P500 (symbol: ^GSPC) from Yahoo Finance. In playgrounds and scripts, the presence of a caret (^) in the string passed to NSURL errors with "Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, sub code=0x0)". Xcode 6b6 and b7.

Works fine with other ticker symbols (AAPL, MSFT, etc).

Any suggestions for how to get this working?

let symbols:String = "^GSPC"
let financeURL:String = "http://finance.yahoo.com/d/quotes.csv?s=\(symbols)&f=sl1c6p2"

var financeNSURL: NSURL = NSURL(string: financeURL) // ERROR (see above)
let tickerNSData: NSData = NSData(contentsOfURL: financeNSURL)

var output:NSString = NSString(data:tickerNSData, encoding:NSUTF8StringEncoding)

回答1:

It's crashing because Swift (at least in Xcode6-Beta7) doesn't support returning nil from an object initializer. From the release notes:

Swift does not support object initializers that fail by returning null. (16480364)

Workaround: If there is a factory method, use it instead. Otherwise, capture the result in an optional. For example:

let url: NSURL? = NSURL(string: "not a url")

So, to avoid a crash, declare your financeNSURL as NSURL? (as in the example from the docs above), or use NSURL.URLWithString() instead of init(string:).

However, the root of the problem is that you're not encoding your URL parameters correctly.

If you call stringByAddingPercentEncodingWithAllowedCharacters(...) on symbols it works:

let symbols:String = "^GSPC".stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
let financeURL:String = "http://finance.yahoo.com/d/quotes.csv?s=\(symbols)&f=sl1c6p2"

let financeNSURL: NSURL? = NSURL(string: financeURL)
if let url = financeNSURL {
    let tickerNSData: NSData = NSData(contentsOfURL: url)

    var output:NSString = NSString(data:tickerNSData, encoding:NSUTF8StringEncoding)
}

Output:

"^GSPC",1999.83,"-2.45","-0.12%"


回答2:

When creating a NSURLwith init method NSURLWithString: the parameter URLString must be a properly encoded URL string. Your's is not. So, what is this a "properly encoded URL string"?

The corresponding official documentation gives a few more hints where to read:

"URLString: The URL string with which to initialize the NSURL object. This URL string must conform to URL format as described in RFC 2396, and must not be nil. This method parses URLString according to RFCs 1738 and 1808." (links are mine).

So basically, you need to separately encode each URL component from your source string component using the correct variant of the percent encoding algorithm. Then, compose all encoded string components to the final URL string.

This can be tedious and is certainly error prone. Thus, since iOS 8 there is NSURLComponents (see docs here) which can aid you in this task.



标签: xcode swift