2016-03-08 8 views
0

J'essaie d'obtenir un fichier JSON à partir d'une URL et de renvoyer le contenu à l'aide de Swift. Toutefois, le code échoue à la ligne let httpResponse = response as! NSHTTPURLResponse dans le code suivant. Je reçois une exception à cette ligne et Xcode passe en mode débogage.Le téléchargement de données JSON échoue sur NSHTTPURLResponse

class func downloadJSONFile()->AnyObject 
    { 
     let requestURL: NSURL = NSURL(string: "http://www.learnswiftonline.com/Samples/subway.json")! 
     let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL) 
     let session = NSURLSession.sharedSession() 
     var json:AnyObject = "" 
     let task = session.dataTaskWithRequest(urlRequest) { 
      (data, response, error) -> Void in 

      let httpResponse = response as! NSHTTPURLResponse 
      let statusCode = httpResponse.statusCode 

      if (statusCode == 200) { 

       do{ 
        json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments) 

       }catch { 
        print("Error with Json: \(error)") 

       } 

      } 

     } 

     task.resume() 

     return json 
    } 

Comment puis-je résoudre ce problème?

+1

Vous ne pas * retour * à partir d'un appel asynchrone. Exemple ici: http://stackoverflow.com/a/35358750/2227743 De plus, vous ne devriez pas forcer la réponse, il se peut qu'elle ne soit pas ici, donc vérifiez d'abord le paramètre d'erreur. – Moritz

Répondre

3

Il y a quelques problèmes:

  1. S'il y a une erreur dans la demande, response aura nil, et donc votre tentative de forcer cast il en résultera une erreur fatale. N'utilisez pas le déballage/moulage forcé pour traiter les réponses réseau.

  2. Il existe un problème plus profond ici que vous essayez de renvoyer des données à partir d'une méthode qui s'exécute de manière asynchrone. Vous devez changer votre méthode de ne pas retourner quoi que ce soit, en soi, mais l'offre plutôt un gestionnaire d'achèvement par lequel vous pouvez de manière asynchrone passer de nouveau les données pertinentes:

    class func downloadJSONFile(completionHandler:(AnyObject?) ->()) { 
        let requestURL: NSURL = NSURL(string: "http://www.learnswiftonline.com/Samples/subway.json")! 
        let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL) 
        let session = NSURLSession.sharedSession() 
        let task = session.dataTaskWithRequest(urlRequest) { data, response, error in 
    
         // check for fundamental networking errors 
    
         guard error == nil && data != nil else { 
          print(error) 
          completionHandler(nil) 
          return 
         } 
    
         // check to see if status code found, and if so, that it's 200 
    
         guard let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200 else { 
          completionHandler(nil) 
          return 
         } 
    
         do { 
          let json = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) 
          completionHandler(json) 
         } catch let parseError as NSError { 
          print("Error with Json: \(parseError)") 
          completionHandler(nil) 
         } 
        } 
    
        task.resume() 
    } 
    

    puis vous l'appelez, en utilisant le gestionnaire d'achèvement (ou utilisation la syntaxe de fermeture arrière, comme indiqué ci-dessous):

    APIClass.downloadJSONFile() { json in 
        guard json != nil else { 
         print("there was some problem") 
         return 
        } 
    
        // now you can use `json` here 
    
        dispatch_async(dispatch_get_main_queue()) { 
         // and if you're doing any model or UI updates, dispatch that back to the main queue 
        } 
    } 
    
    // but do not use `json` here, as the above runs asynchronously 
    

    Notez que si vous vouliez fournir les informations d'erreur à la routine d'appel, vous pouvez changer pour revenir également les informations d'erreur, par exemple:

    enum DownloadError : ErrorType { 
        case NetworkError(NSError?) 
        case NotHTTPResponse 
        case InvalidHTTPResponse(Int) 
        case JSONError(NSError) 
    } 
    
    class func downloadJSONFile(completionHandler:(AnyObject?, ErrorType?) ->()) { 
        let requestURL: NSURL = NSURL(string: "http://www.learnswiftonline.com/Samples/subway.json")! 
        let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL) 
        let session = NSURLSession.sharedSession() 
        let task = session.dataTaskWithRequest(urlRequest) { data, response, error in 
    
         guard error == nil && data != nil else { 
          completionHandler(nil, DownloadError.NetworkError(error)) 
          return 
         } 
    
         guard let httpResponse = response as? NSHTTPURLResponse else { 
          completionHandler(nil, DownloadError.NotHTTPResponse) 
          return 
         } 
    
         guard httpResponse.statusCode == 200 else { 
          completionHandler(nil, DownloadError.InvalidHTTPResponse(httpResponse.statusCode)) 
          return 
         } 
    
         do { 
          let json = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) 
          completionHandler(json, nil) 
         } catch let parseError as NSError { 
          completionHandler(nil, DownloadError.JSONError(parseError)) 
         } 
        } 
    
        task.resume() 
    } 
    

    Et, de toute évidence, l'appel changerait à prendre les deux paramètres:

    APIClass.downloadJSONFile() { json, error in 
        guard json != nil && error == nil else { 
         print("there was some problem \(error)") 
         return 
        } 
    
        // and then it would be like before ... 
    } 
    
  3. Lorsque vous utilisez NSURLSession dans iOS 9 et plus tard, il ne permettra pas les demandes de texte en clair (à savoir "http" n'est pas autorisé, seul "https" est, par défaut). Vous pouvez forcer l'application à autoriser les demandes non-https en ajoutant ce qui suit à votre info.plist. Voir https://stackoverflow.com/a/31254874/1271826 pour plus d'informations

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