I am trying to set up a feed page with a UITableView, retrieving all the JSON data from Node.js API.
Looks like it it is working, but it is very slow and sometimes does not retrieve all the images. Is there a way to make it work completely, and to optimize the code?
import UIKit
class homeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var jsonData : [NSDictionary] = [NSDictionary]()
var imageUrls: NSDictionary = NSDictionary()
var urlsArray: [NSURL]! = [NSURL]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.reloadData()
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
println("This is run on the background queue")
self.refreshData()
self.getImage()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
println("This is run on the main queue, after the previous code in outer block")
self.tableView.reloadData()
})
})
}
override func viewWillAppear(animated: Bool) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return jsonData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var type = jsonData[indexPath.row]["type"] as! Int
if type == 1 {
println("Type= \(type)")
let cell1 : cellTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! cellTableViewCell
//If images url are retrieved, load them. Otherwise, load the placeholders
if self.urlsArray.isEmpty == false {
println("Tiè: \(self.urlsArray[indexPath.row])")
if let data = NSData(contentsOfURL: self.urlsArray[indexPath.row]) {
cell1.profileImg?.image = UIImage(data: data)
}
} else {
cell1.profileImg?.image = UIImage(named: "placeholder.png")
}
cell1.testLbl.text = (self.jsonData[indexPath.row]["author"] as? String)!
return cell1
} else {
let cell2 : cell2TableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell2") as! cell2TableViewCell
return cell2
}
}
func refreshData() {
let requestURL = NSURL(string:"http://adall.ga/api/feeds/author/mat/0")!
var request = NSMutableURLRequest(URL: requestURL)
request.HTTPMethod = "GET"
request.addValue(userToken, forHTTPHeaderField: "tb-token")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) {
data, response, error in
println(response)
var dataString = NSString(data: data, encoding: NSUTF8StringEncoding)
println(dataString)
//let jsonResult : NSDictionary = (NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSDictionary)!
//jsonData = (NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers , error: nil) as? NSArray)!
self.jsonData = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: nil) as! [NSDictionary]
}
task.resume()
var index: Int
for index = 0; index < 10000; ++index {
print("Index: \(index), Task state: \(task.state)")
}
}
func getImage() {
var i = 0
for jsonSingleData in jsonData {
let author = jsonSingleData["author"] as! String
let requestURL2 = NSURL(string: "http://adall.ga/api/users/" + author + "/image")!
println("request: \(requestURL2)")
var request2 = NSMutableURLRequest(URL: requestURL2)
request2.HTTPMethod = "GET"
request2.addValue(userToken!, forHTTPHeaderField: "tb-token")
let session2 = NSURLSession.sharedSession()
let task2 = session2.dataTaskWithRequest(request2) {
data, response, error in
println("response= \(response)")
var dataString = NSString(data: data, encoding: NSUTF8StringEncoding)
println(dataString)
self.imageUrls = (NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary)
//check if exists
let imageUrl = self.imageUrls["url"] as! String
let url = NSURL(string: "http://" + imageUrl)
self.urlsArray.append(url!)
println(self.urlsArray)
}
task2.resume()
}
}
Hi for loading images you can use SDWebImage it will take care of all the heavy lifting and caching for you. here's how:
// Here we use the new provided sd_setImageWithURL: method to load the web image
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {... completion code here ...}];
Here's a Swift example
Related
I build project tableview with Json parsing. My project its work, but my imageview is spread. Before I already setting constraint for it image
Can someone help me how to make image to scale fit with constraint setting
My code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
final let urlString = "http://wisatasumbar.esy.es/restful/search_articles.php?&keyword=bukittinggi"
#IBOutlet weak var tableView: UITableView!
var wisataArr = [String]()
var titleArr = [String]()
var imgUrlArr = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.downloadJsonWithURL()
}
func downloadJsonWithURL () {
let url = NSURL(string: urlString)
URLSession.shared.dataTask(with: (url as? URL)!, completionHandler: { (data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
print(jsonObj!.value(forKey: "data"))
if let dataArray = jsonObj!.value(forKey: "data") as? NSArray {
for data in dataArray{
if let dataDict = data as? NSDictionary{
if let title = dataDict.value(forKey: "title"){
self.titleArr.append(title as! String)
}
if let title = dataDict.value(forKey: "category"){
self.wisataArr.append(title as! String)
}
if let title = dataDict.value(forKey: "image_satu"){
self.imgUrlArr.append(title as! String)
}
}
}
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}).resume()
}
func downloadJsonWitTask () {
let url = NSURL(string: urlString)
var downloadTask = URLRequest(url: (url as? URL)!, cachePolicy: URLRequest.CachePolicy.reloadIgnoringCacheData, timeoutInterval: 20)
downloadTask.httpMethod = "GET"
URLSession.shared.dataTask(with: downloadTask, completionHandler: { (data, response, error) -> Void in
let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments)
print(jsonObj)
}).resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titleArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
cell.lblTitle.text! = titleArr[indexPath.row]
cell.lblWisata.text! = wisataArr[indexPath.row]
let imgUrl = NSURL(string: imgUrlArr[indexPath.row])
if imgUrl != nil {
let data = NSData(contentsOf: imgUrl as! URL)
cell.imgView.image = UIImage(data: data as! Data)
}
return cell
}
}
and this my app:
View Project Running
I am getting build errors when trying to display jSON data in Xcode using Swift 3. I am going to copy a portion of my code to this page with hopes you guys can assist me.
I have found similar questions on this site however answers don't seem to help.
class FilmsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
weak var tableView : UITableView!
var FilmArray = [String]()
let film_url = "https://www.testing.com/api/resources/films/1"
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for:indexPath) as! FilmsAPITableViewCell
// Adding the right informations
cell.movieTitle.text = FilmArray[indexPath.row]
// Returning the cell
return cell
}
// #IBOutlet weak var FilmsView: UITableView!
// weak var tableView : UITableView!
// var FilmArray = [String]()
//
// let film_url = "https://www.distribber.com/api/resources/films/1"
//
override func viewDidLoad() {
super.viewDidLoad()
let tableView = UITableView (frame:view.bounds)
view.addSubview(tableView)
self.tableView = tableView
tableView.dataSource = self
tableView.delegate = self
// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return 1
// }
// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// // Getting the right element
// //let films = FilmArray[indexPath.row]
//
//
// // Instantiate a cell
// //let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "moviecell")
// let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! FilmsAPITableViewCell
// // cell.movieTitle.text = FilmArray[indexPath.row]
// // Adding the right informations
// cell.movieTitle.text = FilmArray[indexPath.row]
// // Returning the cell
// return cell
// }
// }
//}
let url:URL = URL(string: film_url)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.setValue("740c94c51891c02b64d6c78840b478fe0b02fe2c", forHTTPHeaderField: "X-API-KEY")
request.setValue("Basic YmhlZW0uZW5nckBnbWFpbC5jb206YmgzM20=", forHTTPHeaderField: "Authorization")
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = ""
// for (key, value) in post_data
// {
// paramString = paramString + (key as! String) + "=" + (value as! String) + "&"
// }
//
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 {
return
}
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data!, options: [])
// Prasing JSON
var parsedData = try JSONSerialization.jsonObject(with: data!, options: []) as! [String:Any]
print(parsedData)
if let FilmArray = parsedData["films"] as? NSArray {
for movieTitle in FilmArray{
if let filmDict = movieTitle as? NSDictionary{
if let film = filmDict.value(forKey: "title") {
self.FilmArray.append(film as! String)
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}
}
print("Hello")
self.tableView.reloadData()
print(self.FilmArray)
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
if let data_block = server_response["data"] as? NSDictionary
{
if let session_data = data_block["session"] as? String
{
// self.login_session = session_data
let preferences = UserDefaults.standard
preferences.set(session_data, forKey: "session")
// DispatchQueue.main.async(execute: self.LoginDone)
}
}
})
task.resume()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is also output from FilmsAPITableViewCell.swift
import UIKit
import UIKit
class FilmsAPITableViewCell: UITableViewCell {
#IBOutlet weak var movieTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
The reason why movieTitle is nil is because your custom cell class does not have that label outlet. You need to create a UILabel in your .xib (or storyboard) and create an outlet connection inside your custom cell class.
It also seems like no self.tableView.reloadData() exists in your completion block. Try adding that right after the print("Hello") line.
P.S. Don't forget to dispatch the reload to the main queue.
Here is also your code which I edited as to get it working:
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
let url:URL = URL(string: film_url)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.setValue("740c94c51891c02b64d6c78840b478fe0b02fe2c", forHTTPHeaderField: "X-API-KEY")
request.setValue("Basic YmhlZW0uZW5nckBnbWFpbC5jb206YmgzM20=", forHTTPHeaderField: "Authorization")
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = ""
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 {
return
}
var json:Any?
do
{
if let existingData = data {
json = try JSONSerialization.jsonObject(with: existingData, options: [])
}
// Prasing JSON
if let parsedData = json as? [[String:Any]] {
for dict in parsedData {
if let title = dict["title"] as? String {
self.FilmArray.append(title)
}
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
if let data_block = server_response["data"] as? NSDictionary
{
if let session_data = data_block["session"] as? String
{
// self.login_session = session_data
let preferences = UserDefaults.standard
preferences.set(session_data, forKey: "session")
// DispatchQueue.main.async(execute: self.LoginDone)
}
}
})
task.resume()
}
Look at this code:
let url = NSURL(string: physicst.image as String)
if let url = url {
let request = NSMutableURLRequest(URL: url)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let data = data {
cell.imageView?.image = UIImage(data: data)
}
})
task.resume()
}else {
print ("nill URL \(physicst.image)")
}
so I have a string, and that string is a url, and I want to load it.
that code is in a table view cells, so it is being called for each cell.
as you see, i am checking if the url is nil or not, and if not, i am making a print statement. almost all the urls are not nil, exception the following ones (which are complemetly valid)
http://commons.wikimedia.org/wiki/Special:FilePath/Kai_Manne_Börje_Siegbahn.jpg?width=300
http://commons.wikimedia.org/wiki/Special:FilePath/Виталий_Лазаревич_Гинзбург.jpg?width=300
http://commons.wikimedia.org/wiki/Special:FilePath/赤崎記念研究館.jpg?width=300
http://commons.wikimedia.org/wiki/Special:FilePath/Kai_Manne_Börje_Siegbahn.jpg?width=300
http://commons.wikimedia.org/wiki/Special:FilePath/赤崎記念研究館.jpg?width=300
The first thing you may argue is to encode the url, and that is what I did like this:
var image = oneBinding["p"]!!["value"] as! NSString
image = image.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
but then even the urls that were working, stopped working. what i am missing please ?
Update
Here is the whole code of my UITableViewController (it is easy)
class PhysicistTableViewController: UITableViewController {
var physicsts : [Physicst]?
#IBOutlet var physicstsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
loadDataFromDBPedia()
}
func loadDataFromDBPedia() {
let session = NSURLSession.sharedSession()
var url = "http://dbpedia.org/sparql/"
let query = "http://dbpedia.org&query=select * {?o dbo:thumbnail ?p . ?o dbo:award dbr:Nobel_Prize_in_Physics}"
url = url + "?default-graph-uri=" + query.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
url = url + "&format=JSON&CXML_redir_for_subjs=121&CXML_redir_for_hrefs=&timeout=30000&debug=on"
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response ,error) in
if let error = error {
print ("\(error)")
}
if let response = response {
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
print("Status code = \(statusCode)")
}
if let data = data {
do {
let jsonResponse = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
let binding = jsonResponse["results"]!!["bindings"] as! NSArray
for oneBinding in binding {
let name = oneBinding["o"]!!["value"] as! NSString
let image = oneBinding["p"]!!["value"] as! NSString
//image = image.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
let physicst = Physicst(image: image, name: name)
if self.physicsts == nil {
self.physicsts = [Physicst]()
}
self.physicsts!.append(physicst)
}
self.physicstsTableView.reloadData()
}catch _ {
print ("not well json-formatted response")
}
}
})
task.resume()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.physicsts == nil {
return 0
}else {
return self.physicsts!.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("physicstCell")! as UITableViewCell
let row = indexPath.row
let physicst = self.physicsts![row]
cell.textLabel?.text = physicst.name as String
if (physicst.imageData == nil) {
let session = NSURLSession.sharedSession()
let url = NSURL(string: physicst.image as String)
if let url = url {
let request = NSMutableURLRequest(URL: url)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let data = data {
let imageData = UIImage(data: data)
cell.imageView?.image = imageData
physicst.imageData = imageData
self.physicstsTableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
}
})
task.resume()
}else {
print ("nill URL \(physicst.image)")
}
}else {
cell.imageView?.image = physicst.imageData!
}
return cell
}
}
URLHostAllowedCharacterSet only contains this "#%/<>?#\^{|}
And your URL string contains : .. So make custom set for this
let yourString = "http://commons.wikimedia.org/wiki/Special:FilePath/Kai_Manne_Börje_Siegbahn.jpg?width=300"
let customSet = NSCharacterSet(charactersInString:"=\"#%/:<>?#\\^`{|}").invertedSet
let finalString = yourString.stringByAddingPercentEncodingWithAllowedCharacters(customSet)
For more info. check this answer
The method you should use is
stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
Taken from the playground:
NSURL(string:"http://commons.wikimedia.org/wiki/Special:FilePath/Kai_Manne_Börje_Siegbahn.jpg?width=300".stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!)
Gives the result
http://commons.wikimedia.org/wiki/Special:FilePath/Kai_Manne_B%C3%B6rje_Siegbahn.jpg?width=300
Which is valid :)
Adapted to your code it gives:
let url = NSURL(string: (physicst.image as String).stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()!)
Beware of the forced unwrapping tho !
First of all, to avoid my bad English, I uploaded a video showing my problem
http://www.mediafire.com/download/j6krsa274o80ik9/Screen_Recording.mov
Second, I have a UITableViewController, that uses a remote API to download data. the data contains many image URLs, my first problem is that the tableView is not being updated even though i am doing .reloadData() function
my second problem is that in the function:
tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
I download the images, which I had got their urls from the first call, then eventhing works good but I can't see the image unless I clicked on the row
Please see the video, it is easier to understand
Here is my code: (I gave you the full code of my UITableView because, it is simple, and because it has two functions, and they are the ones that making me problems)
class Physicst: NSObject {
let image : String
var imageData: UIImage?
let name : NSString
init(image: String, name: NSString) {
self.image = image
self.name = name
}
}
class PhysicistTableViewController: UITableViewController {
var physicsts : [Physicst]?
#IBOutlet var physicstsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
loadDataFromDBPedia()
}
func loadDataFromDBPedia() {
let session = NSURLSession.sharedSession()
var url = "http://dbpedia.org/sparql/"
let query = "http://dbpedia.org&query=select * {?o dbo:thumbnail ?p . ?o dbo:award dbr:Nobel_Prize_in_Physics} limit 10"
url = url + "?default-graph-uri=" + query.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
url = url + "&format=JSON&CXML_redir_for_subjs=121&CXML_redir_for_hrefs=&timeout=30000&debug=on"
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response ,error) in
if let error = error {
print ("\(error)")
}
if let response = response {
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
print("Status code = \(statusCode)")
}
if let data = data {
do {
let jsonResponse = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
let binding = jsonResponse["results"]!!["bindings"] as! NSArray
for oneBinding in binding {
let name = oneBinding["o"]!!["value"] as! NSString
var image = oneBinding["p"]!!["value"] as! String
image = image.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let physicst = Physicst(image: image, name: name)
if self.physicsts == nil {
self.physicsts = [Physicst]()
}
self.physicsts!.append(physicst)
}
self.physicstsTableView.reloadData()
}catch _ {
print ("not well json-formatted response")
}
}
})
task.resume()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.physicsts == nil {
return 0
}else {
return self.physicsts!.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("physicstCell")! as UITableViewCell
let row = indexPath.row
let physicst = self.physicsts![row]
cell.textLabel?.text = physicst.name as String
if (physicst.imageData == nil) {
let session = NSURLSession.sharedSession()
let url = NSURL(string: physicst.image as String)
if let url = url {
let request = NSMutableURLRequest(URL: url)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let data = data {
let imageData = UIImage(data: data)
cell.imageView?.image = imageData
physicst.imageData = imageData
self.physicstsTableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
}
})
task.resume()
}else {
print ("nill URL \(physicst.image)")
}
}else {
cell.imageView?.image = physicst.imageData!
}
return cell
}
}
**fell free to copy/paste it, there is no custom cell, so it should work **
tableView reload should be called on main thread. session.dataTaskWithRequest completion block is called on background thread performing UI Operations on background thread might lead to serious consequences. I believe the problem you are facing is just one of those consequences. Modify the code as follow.
func loadDataFromDBPedia() {
let session = NSURLSession.sharedSession()
var url = "http://dbpedia.org/sparql/"
let query = "http://dbpedia.org&query=select * {?o dbo:thumbnail ?p . ?o dbo:award dbr:Nobel_Prize_in_Physics} limit 10"
url = url + "?default-graph-uri=" + query.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
url = url + "&format=JSON&CXML_redir_for_subjs=121&CXML_redir_for_hrefs=&timeout=30000&debug=on"
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response ,error) in
if let error = error {
print ("\(error)")
}
if let response = response {
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
print("Status code = \(statusCode)")
}
if let data = data {
do {
let jsonResponse = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
let binding = jsonResponse["results"]!!["bindings"] as! NSArray
for oneBinding in binding {
let name = oneBinding["o"]!!["value"] as! NSString
var image = oneBinding["p"]!!["value"] as! String
image = image.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let physicst = Physicst(image: image, name: name)
if self.physicsts == nil {
self.physicsts = [Physicst]()
}
self.physicsts!.append(physicst)
}
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self.physicstsTableView.reloadData()
}
}catch _ {
print ("not well json-formatted response")
}
}
})
task.resume()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("physicstCell")! as UITableViewCell
let row = indexPath.row
let physicst = self.physicsts![row]
cell.textLabel?.text = physicst.name as String
if (physicst.imageData == nil) {
let session = NSURLSession.sharedSession()
let url = NSURL(string: physicst.image as String)
if let url = url {
let request = NSMutableURLRequest(URL: url)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let data = data {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let imageData = UIImage(data: data)
cell.imageView?.image = imageData
physicst.imageData = imageData
self.physicstsTableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
})
}
})
task.resume()
}else {
print ("nill URL \(physicst.image)")
}
}else {
cell.imageView?.image = physicst.imageData!
}
return cell
}
TIP
Downloading the images manually for each cell and then loading it to tableViewCell and handling caching in order to improve the performance of scroll is like re inventing the wheel when you have tubless tires availabe :) Please consider using SDWebImage or AFNetworking I have personlly used SDWebImage and its caching feature works perfectly.
The view I'm developing does the following:
Sends a GET request to the API to retrieve a list of users
Sends GET requests to the API to retrieve profile images from the list of users
Display the images in TableViewCells
However, I'm having problem managing the tasks and the queues. What is the best way to be sure that all the requests and tasks are done before populating the Table View?
Here's the code:
import UIKit
class homeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var jsonData : [NSDictionary] = [NSDictionary]()
var imageUrls: NSDictionary = NSDictionary()
var urlsArray: [NSURL] = [NSURL]()
override func viewDidLoad() {
super.viewDidLoad()
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
self.refreshData()
self.getImage()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
})
}
override func viewWillAppear(animated: Bool) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return jsonData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var type = jsonData[indexPath.row]["type"] as! Int
for typej in jsonData {
let t : Int = typej["type"] as! Int
println("type : \(t)")
}
if type == 1 {
let cell1 : cellTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! cellTableViewCell
/* //If images url are retrieved, load them. Otherwise, load the placeholders
if self.urlsArray.isEmpty == false {
println("Tiè: \(self.urlsArray[indexPath.row])")
if let data = NSData(contentsOfURL: self.urlsArray[indexPath.row]) {
cell1.profileImg?.image = UIImage(data: data)
}
} else {
cell1.profileImg?.image = UIImage(named: "placeholder.png")
}*/
let block: SDWebImageCompletionBlock! = {
(image: UIImage!, error: NSError!, cacheType: SDImageCacheType, imageURL: NSURL!) -> Void in
println(self)
}
println("url Array: \(self.urlsArray)")
let url = NSURL(string: "http://adall.ga/s/profile-1439584252497.png")
if UIApplication.sharedApplication().canOpenURL(urlsArray[indexPath.row]) {
cell1.profileImg.sd_setImageWithURL(urlsArray[indexPath.row], completed: block)
} else {
cell1.profileImg.sd_setImageWithURL(url, completed: block)
}
cell1.testLbl.text = (self.jsonData[indexPath.row]["author"] as? String)!
return cell1
} else {
let cell2 : cell2TableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell2") as! cell2TableViewCell
return cell2
}
}
func refreshData() {
let requestURL = NSURL(string:"http://adall.ga/api/feeds/author/mat/0")!
var request = NSMutableURLRequest(URL: requestURL)
request.HTTPMethod = "GET"
request.addValue(userToken, forHTTPHeaderField: "tb-token")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) {
data, response, error in
println(response)
var dataString = NSString(data: data, encoding: NSUTF8StringEncoding)
println(dataString)
//let jsonResult : NSDictionary = (NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSDictionary)!
//jsonData = (NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers , error: nil) as? NSArray)!
self.jsonData = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: nil) as! [NSDictionary]
}
task.resume()
var index: Int
for index = 0; index < 10000; ++index {
print("Index: \(index), Task state: \(task.state)")
}
}
func getImage() {
var i = 0
for jsonSingleData in jsonData {
let author = jsonSingleData["author"] as! String
let requestURL2 = NSURL(string: "http://adall.ga/api/users/" + author + "/image")!
var request2 = NSMutableURLRequest(URL: requestURL2)
request2.HTTPMethod = "GET"
request2.addValue(userToken!, forHTTPHeaderField: "tb-token")
let session2 = NSURLSession.sharedSession()
let task2 = session2.dataTaskWithRequest(request2) {
data, response, error in
println("response= \(response)")
var dataString = NSString(data: data, encoding: NSUTF8StringEncoding)
println(dataString)
self.imageUrls = (NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary)
if self.imageUrls["url"] != nil {
//check if exists
let imageUrl = self.imageUrls["url"] as! String
let url = NSURL(string: "http://" + imageUrl)
self.urlsArray.append(url!)
} else {
let imageUrl = "http://shackmanlab.org/wp-content/uploads/2013/07/person-placeholder.jpg"
let url = NSURL(string: imageUrl)
self.urlsArray.append(url!)
}
}
task2.resume()
self.tableView.reloadData()
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
The point of the issue is the following code:
dispatch_async(backgroundQueue, {
self.refreshData()
self.getImage()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
})
The NSURLSession working in the background thread, so your jsonData is empty when the self.getImage() and reloadData are executed.
You can call the self.getImage() after this line
self.jsonData = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: nil) as! [NSDictionary]
in the session.dataTaskWithRequest completed block and calls reloadData(on the dispatch_get_main_queue) in the completed block of the session2.dataTaskWithRequest.
I think this will solved your issue.