Swift iOS 9 UITableView reloadData after CoreData finished - ios

I try to update my tableview after my REST-call is finished and all entities are stored in CoreData. I tried dispatch_asynch but the tableview doesn't reload when expected. I am using a UIRefreshControl to trigger refreshing. Usually, the correct tableview data is displayed after 2 refreshes. I have no idea why.
I can confirm that my tableview instance IS NOT nil.
#IBAction func refreshTriggered(sender: UIRefreshControl) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
self.evMan.retrieveAndSaveEvents(self.pubMan.getAllPublishers())
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
}
}
This is my retrieveAndSaveEvents method from my 'EventManager' evMan:
func retrieveAndSaveEvents(forPublishers: [PUBLISHERS]) {
for publisher in forPublishers {
let pubId = publisher.id as Int
let str = "/publishers/\(pubId)/events"
// resty is an instance of my REST-api wrapper
self.resty.GET(self.server, route: str, onCompletion: {json in
let result = json.asArray
for var i = 0; i < result!.count; i++ {
if !self.isIDAlreadyInDB(json[i]["id"].asInt!) {
let newEv = NSEntityDescription.insertNewObjectForEntityForName("EVENTS", inManagedObjectContext: self.context!) as! EVENTS
newEv.headline = json[i]["headline"].asString!
newEv.id = json[i]["id"].asInt!
newEv.content = json[i]["content"].asString!
newEv.ofPublisher = publisher
do {
try self.context!.save()
} catch _ {
}
print("New Event from \(publisher.name): \(newEv.headline)")
}
}
})
}
}
FYI, here's my cellForRowAtIndexPath: (I am using a Custom UITableViewCell)
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("eventCell") as? EventCell
let curEv = evMan.getEvents()[indexPath.row]
cell?.infoLabel.text = curEv.ofPublisher?.name
cell?.myImageView.image = UIImage(named: "icon.png")
cell?.detailLabel.text = curEv.headline
cell?.timeLabel.attributedText = NSAttributedString(string: self.dateFormatter.stringFromDate(curEv.updatedAt))
cell?.contentView.backgroundColor = UIColor.clearColor()
cell?.backgroundColor = UIColor(white: 1.0, alpha: 0.5)
return cell!
}
Here is my REST-wrapper:
class REST: NSObject {
// Basic Auth
let sConfig = ServerConfig()
var username = "rest"
var password = "resttest"
override init() {
username = sConfig.getUserLogin().user
password = sConfig.getUserLogin().pass
}
func GET(server: String, route: String, onCompletion: (JSON) -> Void) {
let route = server+route
makeHTTPGetRequest(route, onCompletion: { json, err in
onCompletion(json as JSON)
})
}
func makeHTTPGetRequest(path: String, onCompletion: ServiceResponse) {
let request = NSMutableURLRequest(URL: NSURL(string: path)!)
let loginString = NSString(format: "%#:%#", username, password)
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = loginData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions())
let loginvalue = "Basic " + base64LoginString
// add Headers
request.addValue(loginvalue, forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
let json:JSON = JSON(data: data!)
onCompletion(json, error)
})
task.resume()
}
}

#IBAction func refreshTriggered(sender: UIRefreshControl) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
self.evMan.retrieveAndSaveEvents(self.pubMan.getAllPublishers())
})
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
}
Now, you cannot guarantee reloadData() happens after self.evMan.retrieveAndSaveEvents(self.pubMan.getAllPublishers()) , because they are not happen in the same queue. And probably, reloadData() happens before retrieve.
Option 1:
You should put the block :
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
at the end of func retrieveAndSaveEvents(self.pubMan.getAllPublishers())
Option 2 :
#IBAction func refreshTriggered(sender: UIRefreshControl) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
self.evMan.retrieveAndSaveEvents(self.pubMan.getAllPublishers())
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
})
}
EDIT:
I did not see there is another queue in retrieveAndSaveEvents
So, put the
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
at the end of:
self.resty.GET(self.server, route: str, onCompletion: {json in
let result = json.asArray
for var i = 0; i < result!.count; i++ {
if !self.isIDAlreadyInDB(json[i]["id"].asInt!) {
let newEv = NSEntityDescription.insertNewObjectForEntityForName("EVENTS", inManagedObjectContext: self.context!) as! EVENTS
newEv.headline = json[i]["headline"].asString!
newEv.id = json[i]["id"].asInt!
newEv.content = json[i]["content"].asString!
newEv.ofPublisher = publisher
do {
try self.context!.save()
} catch _ {
}
print("New Event from \(publisher.name): \(newEv.headline)")
}
}
})

Related

MessageKit messages keep switching sides

I am implementing a Chat between driver and user in a cab app. I am getting the message from API and displaying it in chat but the chat flips depending on the ID of the last msg
I just want to add the incoming msg on the left side of the screen.
import Foundation
import MessageKit
class ChatViewController: MessagesViewController {
private lazy var loader:UIView = {
return createActivityIndicator(UIScreen.main.focusedView ?? self.view)
}()
#IBOutlet weak var topView: UIView!
let imageView : UIImageView = {
let iv = UIImageView()
iv.image = UIImage(named:"backgroundImg")
iv.contentMode = .scaleAspectFill
return iv
}()
var userId = NSNumber()
var providerId = NSNumber()
var rideId = NSNumber()
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
member = Member(name: "User", color: .headingGold)
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messageInputBar.delegate = self
messagesCollectionView.messagesDisplayDelegate = self
var topSafeAreaHeight: CGFloat = 0
var bottomSafeAreaHeight: CGFloat = 0
let window = UIApplication.shared.windows[0]
if #available(iOS 11.0, *) {
let safeFrame = window.safeAreaLayoutGuide.layoutFrame
topSafeAreaHeight = safeFrame.minY
bottomSafeAreaHeight = safeFrame.maxY
} else {
topSafeAreaHeight = 10
}
self.topView.frame = CGRect(x: 10, y: topSafeAreaHeight, width: self.view.frame.size.width-20, height: 60)
self.topView.layer.cornerRadius = 10
self.messagesCollectionView.contentInset = UIEdgeInsets(top: topView.frame.maxY, left: 0, bottom: 70, right: 0)
messagesCollectionView.backgroundView = imageView
func backgroundColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
return isFromCurrentSender(message: message) ? UIColor.myGold : UIColor.headingGold
}
getChatHistory()
self.timer = Timer.scheduledTimer(timeInterval: 6.0, target: self, selector: #selector(getChatHistory), userInfo: nil, repeats: true)
}
override func viewDidAppear(_ animated: Bool) {
view.bringSubviewToFront(topView)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(true)
timer.invalidate()
}
#objc func getChatHistory(){
self.messages = []
let request = NSMutableURLRequest(url: NSURL(string: "\(ServiceConstants.baseUrl+ServiceConstants.MD_GET_CHAT)?request_id=\("\(self.rideId)")&user_id=\(self.userId)")! as URL)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With")
let tokenStr:NSString = UserDefaults.standard.value(forKey: "tokentype") as! NSString
let accessStr:NSString = UserDefaults.standard.value(forKey: "accesstoken") as! NSString
let str = NSString(format: "%# %#", tokenStr, accessStr) as String
request.addValue(str, forHTTPHeaderField: "Authorization")
let session = URLSession.shared
// self.loader.isHidden = false
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
// DispatchQueue.main.async {
// self.loader.isHidden = true
//
// }
if (error != nil) {
print(error!)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse!)
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
print(jsonResult)
let messagesArray = jsonResult["messages"] as! NSArray
for dictVal in messagesArray
{
if let str = dictVal as? [String:AnyObject]
{
let msg = str["message"] as! String
let type = str["type"] as! String
self.member = Member(name: type, color: .myGold)
let newMessage = Message(
member: self.member,
text: msg,
messageId: UUID().uuidString)
// self.messageCallback(newMessage)
self.messages.append(newMessage)
}
}
DispatchQueue.main.async{
self.messagesCollectionView.reloadData()
self.messagesCollectionView.scrollToBottom(animated: true)
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
})
dataTask.resume()
}
func sendMsg(msg:String){
let parameters:[String:String] = ["user_id":"\(userId)","provider_id":"\(providerId)","request_id":"\(rideId)","type":"up","message":"\(msg)"]
let url = URL(string: ServiceConstants.baseUrl + ServiceConstants.MD_SEND_CHAT_MSG)!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With")
let tokenStr:NSString = UserDefaults.standard.value(forKey: "tokentype") as! NSString
let accessStr:NSString = UserDefaults.standard.value(forKey: "accesstoken") as! NSString
let str = NSString(format: "%# %#", tokenStr, accessStr) as String
request.addValue(str, forHTTPHeaderField: "Authorization")
do{
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
}catch let error
{
print(error.localizedDescription)
}
self.loader.isHidden = false
let task = session.dataTask(with: request, completionHandler: {data, response, error in
DispatchQueue.main.async {
self.loader.isHidden = true
}
print(response as Any)
guard error == nil else{return}
guard let data = data else{return}
do{
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any]
{
print(json)
}
}catch let error
{
print(error.localizedDescription)
}
})
task.resume()
}
var messages: [Message] = []
var member: Member!
#IBAction func backBtnAction(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
extension ChatViewController: MessagesDataSource {
func numberOfSections(
in messagesCollectionView: MessagesCollectionView) -> Int {
return messages.count
}
func currentSender() -> Sender {
return Sender(id: member.name, displayName: member.name)
}
func messageForItem(
at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> MessageType {
return messages[indexPath.section]
}
func messageTopLabelHeight(
for message: MessageType,
at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> CGFloat {
return 12
}
func messageTopLabelAttributedText(
for message: MessageType,
at indexPath: IndexPath) -> NSAttributedString? {
return NSAttributedString(
string: message.sender.displayName,
attributes: [.font: UIFont.systemFont(ofSize: 12)])
}
}
extension ChatViewController: MessagesLayoutDelegate {
func heightForLocation(message: MessageType,
at indexPath: IndexPath,
with maxWidth: CGFloat,
in messagesCollectionView: MessagesCollectionView) -> CGFloat {
return 0
}
}
extension ChatViewController: MessagesDisplayDelegate {
func configureAvatarView(
_ avatarView: AvatarView,
for message: MessageType,
at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) {
let message = messages[indexPath.section]
let color = message.member.color
avatarView.backgroundColor = color
}
}
extension ChatViewController: MessageInputBarDelegate {
func messageInputBar(
_ inputBar: MessageInputBar,
didPressSendButtonWith text: String) {
sendMsg(msg: text)
member = Member(name: "up", color: .myGold)
let newMessage = Message(
member: member,
text: text,
messageId: UUID().uuidString)
messages.append(newMessage)
inputBar.inputTextView.text = ""
messagesCollectionView.reloadData()
messagesCollectionView.scrollToBottom(animated: true)
}
}
struct Member {
let name: String
let color: UIColor
}
struct Message {
let member: Member
let text: String
let messageId: String
}
extension Message: MessageType {
var sender: Sender {
return Sender(id: member.name, displayName: member.name)
}
var sentDate: Date {
return Date()
}
var kind: MessageKind {
return .text(text)
}
}
This is my class for chatViewController and this is my first experience with messageKit. I might be missing something simple or trivial..
The main function that determines alignment is,
func currentSender() -> Sender {
return Sender(id: member.name, displayName: member.name)
}
In this case, member.name is the identifier to recognize own messages.
The primary provider of that data is initialized as,
member = Member(name: "User", color: .headingGold)
But as you insert a new message it got changed to causing logic for alignment to change,
member = Member(name: "up", color: .myGold) // why change global variable?
In this case, if a member with name: "up" sends a text it switches sides. Better refactor the entire codebase and maintain better id for current and counterpart users.
Follow, official Doc implementation for reference.

UITable Won't display. Value type 'FilmsAPITableViewCell' has no member 'movieTitle'

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()
}

Stop activity indicator after load UITableView

How do automatically stop activity indicator after the refresh button is pressed and loading of content is done in UITableview JSON
I already have to the code to start the spinning and the button
Here is full code
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var json_data_url = "http://www.howtotechworld.com/json_table_view_images%20(1).json"
var image_base_url = ""
var isProgressShowing = true;
var TableData:Array< datastruct > = Array < datastruct >()
enum ErrorHandler:ErrorType
{
case ErrorFetchingResults
}
struct datastruct
{
var imageurl:String?
var description:String?
var image:UIImage? = nil
init(add: NSDictionary)
{
imageurl = add["url"] as? String
description = add["description"] as? String
}
}
#IBOutlet var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
tableview.delegate = self
get_data_from_url(json_data_url)
}
override func viewWillAppear(animated: Bool) {
let barButtonItem = UIBarButtonItem(title: "Refresh", style: .Plain, target: self, action: "refreshTapped");
self.navigationItem.rightBarButtonItem = barButtonItem;
}
func refreshTapped() {
addProgressIndicator(isProgressShowing);
get_data_from_url(json_data_url)
}
func addProgressIndicator(show : Bool) {
isProgressShowing = !show;
if(show) {
let myActivityIndicator = UIActivityIndicatorView(activityIndicatorStyle:UIActivityIndicatorViewStyle.Gray)
myActivityIndicator.startAnimating()
let barButtonItem = UIBarButtonItem(customView: myActivityIndicator)
self.navigationItem.rightBarButtonItem = barButtonItem
} else {
self.navigationItem.rightBarButtonItem = nil;
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
let data = TableData[indexPath.row]
cell.textLabel?.text = data.description
if (data.image == nil)
{
cell.imageView?.image = UIImage(named:"image.jpg")
load_image(image_base_url + data.imageurl!, imageview: cell.imageView!, index: indexPath.row)
}
else
{
cell.imageView?.image = TableData[indexPath.row].image
}
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return TableData.count
}
func get_data_from_url(url:String)
{
let url:NSURL = NSURL(string: url)!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let task = session.dataTaskWithRequest(request) {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
dispatch_async(dispatch_get_main_queue(), {
self.extract_json(data!)
return
})
}
task.resume()
}
func extract_json(jsonData:NSData)
{
let json: AnyObject?
do {
json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
} catch {
json = nil
return
}
if let list = json as? NSArray
{
for (var i = 0; i < list.count ; i++ )
{
if let data_block = list[i] as? NSDictionary
{
TableData.append(datastruct(add: data_block))
}
}
do
{
try read()
}
catch
{
}
do_table_refresh()
}
}
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.tableview.reloadData()
return
})
}
func load_image(urlString:String, imageview:UIImageView, index:NSInteger)
{
let url:NSURL = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
let task = session.downloadTaskWithURL(url) {
(
let location, let response, let error) in
guard let _:NSURL = location, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let imageData = NSData(contentsOfURL: location!)
dispatch_async(dispatch_get_main_queue(), {
self.TableData[index].image = UIImage(data: imageData!)
self.save(index,image: self.TableData[index].image!)
imageview.image = self.TableData[index].image
return
})
}
task.resume()
}
func read() throws
{
do
{
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName: "Images")
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest)
for (var i=0; i < fetchedResults.count; i++)
{
let single_result = fetchedResults[i]
let index = single_result.valueForKey("index") as! NSInteger
let img: NSData? = single_result.valueForKey("image") as? NSData
TableData[index].image = UIImage(data: img!)
}
}
catch
{
print("error")
throw ErrorHandler.ErrorFetchingResults
}
}
func save(id:Int,image:UIImage)
{
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let entity = NSEntityDescription.entityForName("Images",
inManagedObjectContext: managedContext)
let options = NSManagedObject(entity: entity!,
insertIntoManagedObjectContext:managedContext)
let newImageData = UIImageJPEGRepresentation(image,1)
options.setValue(id, forKey: "index")
options.setValue(newImageData, forKey: "image")
do {
try managedContext.save()
} catch
{
print("error")
}
}
}
You will need to keep a reference to myActivityIndicator and stop it in do_table_refresh() as shown below.
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.tableview.reloadData()
myActivityIndicator.stopAnimating()
return
})
}
Edit: Just had a quick thought after looking at your code in the question.
If you would like to hide myActivitIndicator using addProgressIndicator(isProgressShowing) , you will have to make sure that isProgressShowing is set to FALSE, then in the code that I have given above, replace myActivityIndicator.stopAnimating() with addProgressIndicator(isProgressShowing).
You will also need to update the addProgressIndicator function and add myActivityIndicator.stopAnimating() to the else condition.
If I understand your question right then I think below is what you want.
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath row] == ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row){
//end of loading
//for example [activityIndicator stopAnimating];
}
}
and for swift it is like this
func cellForRowAtIndexPath(_ indexPath: NSIndexPath) -> UITableViewCell?{
if(indexPath.row = tableView.indexPathsForVisibleRows().lastObject().row{
//end of loading
//for example [activityIndicator stopAnimating];
}
return yourCell;
}
I don't know swift much, but i guess you will have got the idea how to do it.
Add this code after do_table_refresh(): addProgressIndicator(!isProgressShowing);

Swift - Manage tasks to populate UITableView

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.

how do i load 5 rows in UItableview and after scroll down again i will load 5 more rows in swift?

Below is the my code!!!
import UIKit
class myhomefeedViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableData = []
var imageCache = [String:UIImage]()
var imageCache1 = [String:UIImage]()
var imageCache2 = [String:UIImage]()
#IBOutlet var appsTableView: UITableView!
var refreshControl:UIRefreshControl!
let PageSize = 5
#IBOutlet var tableViewFooter:Reload!
var loading = false
override func viewDidLoad() {
super.viewDidLoad()
self.tableViewFooter.hidden = true
loadmyhomefeeddata(0, size: PageSize)
self.refreshControl = UIRefreshControl()
self.refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
self.refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
self.appsTableView.addSubview(refreshControl)
}
func refresh(refreshControl: UIRefreshControl) {
loadmyhomefeeddata(0, size: PageSize)
refreshControl.endRefreshing()
}
func loadSegment(offset:Int, size:Int) {
if (!self.loading) {
self.setLoadingState(true)
if currentpage < toPage {
}
else if currentpage > toPage
{
self.setLoadingState(false)
}
}
else
{
println("Not Loading")
}
}
func setLoadingState(loading:Bool) {
self.loading = loading
self.tableViewFooter.hidden = !loading
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let currentOffset = scrollView.contentOffset.y
let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
if (maximumOffset - currentOffset) <= 40 {
loadSegment(currentpage, size: tableData.count)
}
}
// pull to refresh list
#IBAction func writeyouridea(sender: AnyObject) {
let viewController=self.storyboard?.instantiateViewControllerWithIdentifier("writeyouridea") as? UIViewController
self.presentViewController(viewController!, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return tableData.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myhomefeedcell", forIndexPath: indexPath) as! myhomefeedTableViewCell
// Configure the cell...
if let rowData: NSDictionary = self.tableData[indexPath.row] as? NSDictionary,
urlString = rowData["imgfile"] as? String,
imgURL = NSURL(string: urlString),
// imgData = NSData(contentsOfURL: imgURL),
countryString = rowData["cflag"] as? String,
countryimgURL = NSURL(string: countryString),
// countryimgData = NSData(contentsOfURL: countryimgURL),
ideaimageString = rowData["ideapicture"] as? String,
ideaimageURL = NSURL(string: ideaimageString),
// ideaimagedata = NSData(contentsOfURL: ideaimageURL),
userfullname = rowData["name"] as? String,
category = rowData["categoryname"] as? String,
ideadesc = rowData["idea"] as? String,
time = rowData["createddate"] as? String,
ideatitle = rowData["title"] as? String {
cell.ideadesc.text = ideadesc
cell.username.text = userfullname
cell.categoryname.text = category
cell.feedimage.image = UIImage(named: "cross")
cell.userimage.image = UIImage(named: "cross")
cell.country.image = UIImage(named: "cross")
cell.title.text = ideatitle
cell.time.text = time
//country
if let countryimg = imageCache1[countryString] {
cell.country.image = countryimg
}
else {
let request: NSURLRequest = NSURLRequest(URL: countryimgURL)
let mainQueue = NSOperationQueue.mainQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
if error == nil {
let imagecountry = UIImage(data: data)
self.imageCache1[countryString] = imagecountry
// Update the cell
dispatch_async(dispatch_get_main_queue(), {
cell.country.image = imagecountry
})
}
else {
println("Error: \(error.localizedDescription)")
}
})
}
//userimage
if let userimg = imageCache2[urlString] {
cell.userimage.image = userimg
}
else {
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let mainQueue = NSOperationQueue.mainQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
if error == nil {
let imageuser = UIImage(data: data)
self.imageCache2[urlString] = imageuser
// Update the cell
dispatch_async(dispatch_get_main_queue(), {
cell.userimage.image = imageuser
})
}
else {
println("Error: \(error.localizedDescription)")
}
})
}
if cell.feedimage.image != nil
{
if let img = imageCache[ideaimageString] {
cell.feedimage.image = img
}
else {
let request: NSURLRequest = NSURLRequest(URL: ideaimageURL)
let mainQueue = NSOperationQueue.mainQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
if error == nil {
let image = UIImage(data: data)
self.imageCache[ideaimageString] = image
// Update the cell
dispatch_async(dispatch_get_main_queue(), {
cell.feedimage.image = image
})
}
else {
println("Error: \(error.localizedDescription)")
}
})
}
}
}
return cell
}
func loadmyhomefeeddata(offset:Int, size:Int) {
let rowslimit = size
let urlPath = "url address?noofrowslimit=\(rowslimit)"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if(error != nil) {
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
}
var err: NSError?
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary {
if(err != nil) {
// If there is an error parsing JSON, print it to the console
println("JSON Error \(err!.localizedDescription)")
}
// println(jsonResult)
if let results: NSArray = jsonResult["results"] as? NSArray {
dispatch_async(dispatch_get_main_queue(), {
self.tableData = results
self.appsTableView!.reloadData()
})
}
}
})
task.resume()
}
}
Are you trying to have a infinite scroll? If so, you could try the following. First of all, make the task an instance variable of your controller then add the following UIScrollViewDelegate.
override func scrollViewDidScroll(scrollView: UIScrollView) {
// If you are already loading elements, return. This method is called multiple times
if task?.state == .Running { // You could use the isLoading variable instead
return
}
let offSetY = scrollView.contentOffset.y
let triggerY = scrollView.contentSize.height - tableView.frame.size.height
if (offSetY >= triggerY) {
// The offset of elements should be the amount you currently have, and you want to fetch 5 more elements
self.loadmyhomefeeddata(tableData.count, size: 5)
}
}

Resources