Tab with UITableView takes long to load - ios

I have a tab that contains a UITableView. The UITableView loads JSON from a server. Here are some lines in my viewDidLoad():
// Register the UITableViewCell class with the tableView
self.tableView?.registerClass(UITableViewCell.self, forCellReuseIdentifier: self.cellIdentifier)
var tblView = UIView(frame: CGRectZero)
tableView.tableFooterView = tblView
tableView.backgroundColor = UIColor.clearColor()
startConnection()
Here is my startConnection():
func startConnection() {
let url = NSURL(string: "some correct URL")
var request = NSURLRequest(URL: url!)
var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)
if data != nil {
var json = JSON(data: data!)
if let jsonArray = json.arrayValue {
for jsonDict in jsonArray {
var pageName: String? = jsonDict["title"].stringValue
//some code
}
}
activityIndicator.stopAnimating()
} else {
println("No data")
activityIndicator.stopAnimating()
var alert = UIAlertController(title: "No data", message: "No data received", preferredStyle: UIAlertControllerStyle.Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in
}
alert.addAction(cancelAction)
let OKAction = UIAlertAction(title: "Retry", style: .Default) { (action) in
self.startConnection()
}
alert.addAction(OKAction)
self.presentViewController(alert, animated: true, completion: nil)
}
}
After the first load, the tab will show upon clicking. I am thinking if it is the NSURLConnection.sendSynchronousRequest causing the lag. Any advice and suggestion? I don't really know how to use sendAsynchronousRequest either =/ Please help. Thank you! =D

Although there are several ways to get data from a website, the important thing is that you execute your UI updates on the main thread using dispatch_async(dispatch_get_main_queue()) {}. This is because URL tasks such as NSURLSession.dataTaskWithURL or NSURLConnection.sendAsynchronousRequest() {} execute on a background thread so if you don't explicitly update UI on the main thread you will often experience a lag. Here's what a simple request looks like:
func fetchJSON(sender: AnyObject?) {
let session = NSURLSession.sharedSession()
let url: NSURL! = NSURL(string: "www.someurl.com")
session.dataTaskWithURL(url) { (data, response, error) in
var rawJSON: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: .allZeros, error: nil)
if let result = rawJSON as? [[String: AnyObject]] {
dispatch_async(dispatch_get_main_queue()) {
// Update your UI here ie. tableView.reloadData()
}
}
}.resume()
}

You can do it like this:
let url:NSURL = NSURL(string:"some url")
let request:NSURLRequest = NSURLRequest(URL:url)
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
/* Your code */
})

Related

why i get different image when downloading to the same path?

I am trying to debug a chunk of code used to upload an image and download that image from my own server.
The image path is "http://localhost/Twitter/Avatar/52/avatar.jpeg"
as we can see, there are two images in that folder, same image but different name. I got a weird result when I hard coded the path when downloading the image
if avatarPath != nil {
let x = "http://localhost/Twitter/Avatar/52/avatar.jpeg"
let imageURL = URL(string: x)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: imageURL!, completionHandler: { (data, response, error) in
DispatchQueue.main.async {
if let imageData = data {
self.avatarImage.image = UIImage(data: imageData)
}
}
})
task.resume()
}
// round courner of avatar
avatarImage.layer.cornerRadius = avatarImage.bounds.width/20
avatarImage.clipsToBounds = true
//Give title to navigation controller
self.navigationItem.title = username.uppercased()
activityIndicator.stopAnimating()
}
when I write let x = "http://localhost/Twitter/Avatar/52/pogba.jpeg"
I go the same image as the path, like this
but when I change to let x = "http://localhost/Twitter/Avatar/52/avatar.jpeg"
I got different image, like this
I once used that image actually when the first time uploading an image, but I don't know why that image appears again. I have not implemented caching image yet. why this happens?
here is the full source code
import UIKit
class HomepageVC: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet weak var avatarImage: UIImageView!
#IBOutlet weak var usernameLabel: UILabel!
#IBOutlet weak var fullnameLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
#IBOutlet weak var editAvatarButton: UIButton!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator.startAnimating()
// mendeklarasikan variable user yang berasal dari superglobal variable di appdelegate
let username = userInfo?["username"] as! String
let fullname = userInfo?["fullname"] as! String
let email = userInfo?["email"] as! String
let avatarPath = userInfo?["avatar"] as? String
// update user interface text & Label
usernameLabel.text = username.uppercased()
fullnameLabel.text = fullname.capitalized
emailLabel.text = email
// update user interface avatar
if avatarPath != nil {
let x = "http://localhost/Twitter/Avatar/52/pogba.jpeg"
let imageURL = URL(string: x)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: imageURL!, completionHandler: { (data, response, error) in
DispatchQueue.main.async {
if let imageData = data {
self.avatarImage.image = UIImage(data: imageData)
}
}
})
task.resume()
}
// round courner of avatar
avatarImage.layer.cornerRadius = avatarImage.bounds.width/20
avatarImage.clipsToBounds = true
//Give title to navigation controller
self.navigationItem.title = username.uppercased()
activityIndicator.stopAnimating()
}
#IBAction func logoutButtonDidPressed(_ sender: Any) {
//menghapus data userDefault yang sudah ada
UserDefaults.standard.removeObject(forKey: "parsedJSON")
UserDefaults.standard.synchronize()
//menuju ke login page dengan modal segue
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = storyboard.instantiateViewController(withIdentifier: "loginVC")
present(loginVC, animated: true, completion: nil)
}
#IBAction func editProfilePictureButtonDidPressed(_ sender: Any) {
// user akan memilih photo dari library atau dari camera nya
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.allowsEditing = true
let actionSheet = UIAlertController(title: "Photo Source", message: "please choose your source", preferredStyle: .actionSheet)
// action camera
let actionCamera = UIAlertAction(title: "Camera", style: .default) { (action) in
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePickerController.sourceType = .camera
self.present(imagePickerController, animated: true, completion: nil)
} else {
self.showAlert(alertTitle: "Opppss", alertMessage: "camera can't be used / not available", actionTitle: "OK")
print("camera can't be used / not available")
}
}
// action photo library
let actionPhotoLibrary = UIAlertAction(title: "Photo Library", style: .default) { (action) in
imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)
}
//action cancel
let actionCancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
actionSheet.addAction(actionCamera)
actionSheet.addAction(actionPhotoLibrary)
actionSheet.addAction(actionCancel)
self.present(actionSheet, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
avatarImage.image = image
picker.dismiss(animated: true, completion: nil)
// call func of uploading file to server
uploadAvatar()
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
// custom HTTP request body to upload image file
func createBodyWithParams(_ parameters: [String: String]?, filePathKey: String?, imageDataKey: Data, boundary: String) -> Data {
var body = Data();
if parameters != nil {
for (key, value) in parameters! {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
}
// kita set agar image yang di upload kemudian berformat .jpg
let filename = "avatar.jpeg"
let mimetype = "image/jpeg"
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimetype)\r\n\r\n")
body.append(imageDataKey)
body.appendString("\r\n")
body.appendString("--\(boundary)--\r\n")
return body as Data
}
// uploading image ke server
func uploadAvatar() {
// mendapatkan ID dari User Default variable
let id = userInfo!["id"] as! String
// membuat request
let url = URL(string: "http://localhost/Twitter/uploadAvatar.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// parameter yang akan dikirim di dalam request body
// parameter ini dibutuhkan karena uploadAvatar.php membutuhkan inputan ID
let param = ["id" : id]
// membuat Boundary
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
// mengassign image yang akan di upload dan melakukan kompresi
let imageData = UIImageJPEGRepresentation(avatarImage.image!, 0.5)
// if not compressed, return ... do not continue to code
if imageData == nil {
return
}
// constructing http body
request.httpBody = createBodyWithParams(param, filePathKey: "file", imageDataKey: imageData!, boundary: boundary)
// filePathKey berupa 'file' agar nanti di PHP $_FILES bisa didentifikasi, contohnya $_FILES['file'][tmp_name]
// launc session
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
// maka tampilkan $returnArray dari PHP (response message from server)
do {
// json containes $returnArray from php
let json = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
// declare new parseJSON to store json
guard let parsedJSON = json else {
print("Error while parsing")
return
}
print(parsedJSON)
// get id from $returnArray["id"] in PHP - parseJSON["id"]
let id = parsedJSON["id"]
// successfully uploaded
if id != nil {
// save user information yang berasal dari server
UserDefaults.standard.set(parsedJSON, forKey: "parsedJSON")
userInfo = UserDefaults.standard.object(forKey: "parsedJSON") as? NSDictionary
// jika tidak ada "id" kiriman dari server, maka ada error message
} else {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = parsedJSON["message"] as! String
self.showAlert(alertTitle: "opppps", alertMessage: message, actionTitle: "OK")
})
}
// error ketika melakukan JSON serialization
} catch {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = error.localizedDescription
self.showAlert(alertTitle: "SorryBroooo", alertMessage: message, actionTitle: "OK")
})
}
// error ketika koneksi ke server
} else {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = error!.localizedDescription
self.showAlert(alertTitle: "oppps", alertMessage: message, actionTitle: "OK")
})
}
})
}.resume()
}
}
// extend data
extension Data {
mutating func appendString(_ string : String) {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
append(data!)
}
}
When you use this code
let session = URLSession(configuration: .default)
you had automatically signed up for default caching policies, it uses persistent disk based cache as specified in this link:
https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1411560-default
if you want to remove all the caching policies, use this code instead
let session = URLSession(configuration: .ephemeral)

Get value from alert textField

I'm currently trying to get a value from an alert box in swift 3.
The below code is used to prompt the alert and save the data, however, im having trouble with calling back the data and manipulating it so it's just a basic string.
func presentAlert() {
let alertController = UIAlertController(title: "IP?", message: "Please input your unique key:", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (_) in
if let field = alertController.textFields?[0] {
// store it
UserDefaults.standard.set(field.text, forKey: "userIP")
UserDefaults.standard.synchronize()
} else {
// user did not fill field
print("no input given")
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
alertController.addTextField { (textField) in
textField.placeholder = "IP"
}
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
This method is called here:
override func viewDidAppear(_ animated: Bool) {
presentAlert()
}
I'm trying to call it and assign it in between a string as:
let url_to_unlock:String = "http://\(UserDefaults.standard.value(forKey: "userIP")):3000/unLock"
However, this gives me the output:
http://Optional():3000/unLock
When I try to print it.
Any nudge in the correct direction would be greatly appreciated.
Class Added:
class ViewController: UIViewController {
func presentAlert() {
let alertController = UIAlertController(title: "IP?", message: "Please input your unique key:", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (_) in
if let field = alertController.textFields?[0] {
// store your data
//this could be lock unique key name etc in future
UserDefaults.standard.set(field.text, forKey: "userIP")
UserDefaults.standard.synchronize()
} else {
// user did not fill field
print("no input given")
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
alertController.addTextField { (textField) in
textField.placeholder = "IP"
}
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
//view did appear for the alert
override func viewDidAppear(_ animated: Bool) {
presentAlert()
}
//to post to an /unLock it must be put in the URL
// let url_to_unlock:String = "http://\(UserDefaults.standard.value(forKey: "userIP")):3000/unLock"
//let url_to_lock:String = "http://\(textField):3000/Lock"
let url_to_unlock:String = "http://10.73.195.218:3000/unLock"
let url_to_lock:String = "http://10.73.195.218:3000/Lock"
override func viewDidLoad() {
super.viewDidLoad()
}
var Timestamp: String {
return "\(NSDate().timeIntervalSince1970 * 1000)"
}
func un_lock()
{
print(url_to_unlock)
let url:URL = URL(string: url_to_unlock)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = "data=unLocking at \(Timestamp)"
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
print("error")
return
}
//for errors
let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print(dataString! )
})
task.resume()
}
func lock()
{
let url:URL = URL(string: url_to_lock)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = "data=Locking at \(Timestamp)"
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
print("error")
return
}
//for errors
let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print(dataString! )
})
task.resume()
}
#IBAction func lock(_ sender: UIButton) {
lock()
}
#IBAction func unLock(_ sender: Any) {
un_lock()
}
}
Thank you.
The value for this is optional:
// let url_to_unlock:String = "http://\(UserDefaults.standard.value(forKey: "userIP")):3000/unLock"
try:
let url = UserDefaults.standard.value(forKey: "userIP")!
let url_to_unlock:String = "http://\(url):3000/unLock"

How to use closures to return a JSON value from a NSURLSession

I am trying to return a result from a JSON object but unable to do so. I am new to Swift so kindly explain me how to do so. In the below code I want to return json_level_number in the return of function fetchNumberOfSections () where i have hard coded as 5 right now return 5. If i declare a variable json_level_number just above the reachability code it sort of solves the problem but then it is returning '0' for the first time. The API returns 2 each time.
Code as below:
func fetchNumberOfSections () -> Int {
if Reachability.isConnectedToNetwork() == true {
// Below code to fetch number of sections
var urlAsString = "http://themostplayed.com/rest/fetch_challenge_sections.php"
urlAsString = urlAsString+"?apiKey="+apiKey
print (urlAsString)
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
print(error!.localizedDescription)
}
do {
let jsonResult = (try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
let json_level_number: String! = jsonResult["level_number"] as! String
//
dispatch_async(dispatch_get_main_queue(), {
// self.dateLabel.text = jsonDate
// self.timeLabel.text = jsonTime
print(json_level_number)
// self.activityIndicatorStop()
})
}
catch let errorJSON {
print (errorJSON)
// alert box code below
let alert = UIAlertController(title: "JSON Error!", message:"Error processing JSON.", preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default) { _ in
// Put here any code that you would like to execute when
self.dismissViewControllerAnimated(true, completion: {})
}
alert.addAction(action)
self.presentViewController(alert, animated: true, completion: nil)
// alert box code end
}
})
jsonQuery.resume()
// End
}
else {
print("Internet connection FAILED")
self.performSegueWithIdentifier("nointernet", sender: nil)
}
return 5
}

Swift Convert string into UIIMAGE

I would like to load an image from an api web service asynchronously into a uitableview with swift for iOS 9. Below is the code from my Playlist controller. Thanks in advance.
import UIKit
class PlaylistViewController: UITableViewController {
var playlists = [[String: String]]()
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "http://xxxxxxx.xxx/api/v1/players/1/playlists?api_key=xxxxxxxxxxxx"
if let url = NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: []) {
let json = JSON(data: data)
if json != nil {
parseJSON(json)
} else {
showError()
}
} else {
showError()
}
} else {
showError()
}
}
func showError() {
let ac = UIAlertController(title: "Loading error", message: "There was a problem loading the feed; please check your connection and try again.", preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil)
}
func parseJSON(json: JSON) {
for result in json["playlists"].arrayValue {
let title = result["title"].stringValue
let id = result["id"].stringValue
let cover_url = result["cover_url"].stringValue
let obj = ["title": title, "id": id, "cover_url" : cover_url]
playlists.append(obj)
}
tableView.reloadData()
}
Use NSURLSession dataTaskWithURL for asynchronously task:
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "http://xxxxxxx.xxx/api/v1/players/1/playlists?api_key=xxxxxxxxxxxx"
if let url = NSURL(string: urlString) {
let session = NSURLSession.sharedSession()
var task = session.dataTaskWithURL(url) { (data, response, error) -> Void in
if let err = error {
showError(err)
} else {
let json = NSString(data: data, encoding: NSUTF8StringEncoding)
// json is a String, you should handle this String as JSON
parseJSON(json)
}
}
}
Your tableView.reloadData() should be executed in main thread (because the NSURLSession dataTaskWithUrl result is in background thread)
dispatch_async(dispatch_get_main_queue(), {
tableView.reloadData()
})

Get Nil When Trying to Reuse NSURL

I'm trying to get the weather of some locations using https://developer.forecast.io/ api.
The format of API call is https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE
I can get response from one position, but when I tried to change the location and get the weather again use NSURL, the NSURL returns Nil. Why is that and how to handle this?
Can anyone helps me out? Thanks.
func getCurrentWeatherData() -> Void {
let baseURL = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)/")
var forecastURL = NSURL(string: "36.107728,-112.113040", relativeToURL: baseURL)
let sharedSession = NSURLSession.sharedSession()
let downloadTask: NSURLSessionDownloadTask = sharedSession.downloadTaskWithURL(forecastURL!, completionHandler: {
(location: NSURL!, response: NSURLResponse!, error: NSError!) -> Void in
//var urlContents = NSString(contentsOfURL: location, encoding: NSUTF8StringEncoding, error: nil)
if (error == nil) {
let dataObject = NSData(contentsOfURL: location)
let weatherDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataObject!, options: nil, error: nil) as NSDictionary
let currentWeather = Current(weatherDictionary: weatherDictionary)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.iconView.image = currentWeather.icon!
self.currentTimeLabel.text = "At \(currentWeather.currentTime!) it is"
self.temperatureLabel.text = "\(Double(currentWeather.temperature-32) * 0.56)"
self.summaryLabel.text = "\(currentWeather.summary)"
self.refreshActivityIndicator.stopAnimating()
self.refreshActivityIndicator.hidden = true
self.refreshButton.hidden = false
})
} else {
let networkIssueController = UIAlertController(title: "Error", message:"Unable to load data. Connectivity error!", preferredStyle: .Alert)
let okButton = UIAlertAction(title: "OK", style: .Default, handler:nil)
networkIssueController.addAction(okButton)
let cancelButton = UIAlertAction(title:"Cancel", style: .Cancel, handler:nil)
networkIssueController.addAction(cancelButton)
self.presentViewController(networkIssueController, animated: true, completion: nil)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.refreshActivityIndicator.stopAnimating()
self.refreshActivityIndicator.hidden = true
self.refreshButton.hidden = false
})
}
})
downloadTask.resume()
var forecastURL2 = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)/36.861941, -111.374420")
let sharedSession2 = NSURLSession.sharedSession()
**//forcastURL2 returns Nil**
let downloadTask2: NSURLSessionDownloadTask = sharedSession2.downloadTaskWithURL(forecastURL2!, completionHandler: {
(location: NSURL!, response: NSURLResponse!, error: NSError!) -> Void in
//var urlContents = NSString(contentsOfURL: location, encoding: NSUTF8StringEncoding, error: nil)
if (error == nil) {
let dataObject = NSData(contentsOfURL: location)
let weatherDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataObject!, options: nil, error: nil) as NSDictionary
let currentWeather = Current(weatherDictionary: weatherDictionary)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.iconView2.image = currentWeather.icon!
self.temperatureLabel2.text = "\(Double(currentWeather.temperature-32) * 0.56)"
self.summaryLabel.text = "\(currentWeather.summary)"
self.refreshActivityIndicator.stopAnimating()
self.refreshActivityIndicator.hidden = true
self.refreshButton.hidden = false
})
} else {
let networkIssueController = UIAlertController(title: "Error", message:"Unable to load data. Connectivity error!", preferredStyle: .Alert)
let okButton = UIAlertAction(title: "OK", style: .Default, handler:nil)
networkIssueController.addAction(okButton)
let cancelButton = UIAlertAction(title:"Cancel", style: .Cancel, handler:nil)
networkIssueController.addAction(cancelButton)
self.presentViewController(networkIssueController, animated: true, completion: nil)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.refreshActivityIndicator.stopAnimating()
self.refreshActivityIndicator.hidden = true
self.refreshButton.hidden = false
})
}
})
downloadTask2.resume()
}
The URL that you use ("https://api.forecast.io/forecast/\(apiKey)/36.861941, -111.374420") should be well formatted and, according to Apple, comply with RFC2396. If not NSURL will return nil.
The url that you use is not correct. For example space should be escape using "%20". In your case, I think you can just remove the space.
Delete the space:
var forecastURL2 = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)/36.861941,-111.374420")
Also, for future questions, it would be helpful if you didn't post all of the code in your method, but rather just the code necessary to reproduce the issue (in this case, just this one line was necessary.)

Resources