Swift 2 : NSData(contentsOfURL:url) returning nil

7.1k views Asked by At

I have a webpage address which contains a JSON file. I'm trying to access the JSON file as NSDictionary in Swift 2. Every time I call NSData(contentsOfURL:url!) where url is type of NSURL?, it returns nil. I'm using Xcode 7 Beta, and the project was first made in Xcode 6.

The following code is causing the problem.

let url = NSURL(string:myurl) // myurl is the webpage address.
let data = NSData(contentsOfURL:url!) // the bit that returns nil
// data is set to nil
// I would perform NSJSONSerialization.JSONObjectWithData later.

What is confusing me is that when I try the exact same thing when I type the same code in Terminal using swift, the constant data is not set to nil. I have tried rebooting the Mac, and it didn't work. I tried reinstalling Xcode, and it didn't work.

This is what happens when I type the following code to the Terminal, using swift keyword.

$> swift
......
Welcome to Apple Swift version 2.0 (700.0.38.1 700.0.53). Type :help for assistance. 

1> import Foundation
2> var urlstr = "http://mywebsiteaddress/jsonfile.json"
3> var nsurl = NSURL(string:urlstr)
nsurl: NSURL? = "http://mywebsiteaddress/jsonfile.json"{
    ObjectiveC.NSObject = {...}
}
4> var nsdata = NSData(contentsOfURL:nsurl!)
nsdata: NSData? = 5925 bytes {
    ObjectiveC.NSObject = {...}
}
5> print(nsdata)
Optional(<Some Values..........>)

When I try in the Terminal it definitely worked. Can anyone help me solve the problem??

1

There are 1 answers

1
Mick MacCallum On BEST ANSWER

I would expect it to work in the terminal, since what you're seeing here is likely not a bug in Swift or Cocoa Touch, but the side effects of a new feature in iOS 9 called App Transport Security. What this means is that by default, iOS will not permit you to make requests servers not secured with SSL.

Quote from link:

App Transport Security (ATS) lets an app add a declaration to its Info.plist file that specifies the domains with which it needs secure communication. ATS prevents accidental disclosure, provides secure default behavior, and is easy to adopt. You should adopt ATS as soon as possible, regardless of whether you’re creating a new app or updating an existing one.

If you’re developing a new app, you should use HTTPS exclusively. If you have an existing app, you should use HTTPS as much as you can right now, and create a plan for migrating the rest of your app as soon as possible.

To fix this, you can edit your info.plist file to make exceptions on a domain by domain basis, or disable App Transport Security entirely. Here's an example quoted from CFNetwork SSLHandshake failed iOS 9 Beta 1.

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>yourserver.com</key>
    <dict>
      <!--Include to allow subdomains-->
      <key>NSIncludesSubdomains</key>
      <true/>
      <!--Include to allow insecure HTTP requests-->
      <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
      <true/>
      <!--Include to specify minimum TLS version-->
      <key>NSTemporaryExceptionMinimumTLSVersion</key>
      <string>TLSv1.1</string>
    </dict>
  </dict>
</dict>

Although I advise that you don't actually use this as your solution and instead use an SSL secured https URL instead.