How do I update tableview cell when It had changed - swift

When I scroll up&down then tableview cell has set overtimes so It affect lagging.
I want to update only when the data is updated.
Post.swift (It is model)
import Foundation
import Parse
class Post: PFObject, PFSubclassing {
#NSManaged var postBy: PFUser?
#NSManaged var postUser: String?
#NSManaged var postUserImg: PFFile?
#NSManaged var postText: String?
#NSManaged var postImg: PFFile?
#NSManaged var sell: NSNumber?
#NSManaged var commentPointer: PFObject?
#NSManaged var commentBy: String?
#NSManaged var comment: String?
#NSManaged var liked: NSArray?
#NSManaged var likeCount: NSNumber?
#NSManaged var mention: NSArray?
#NSManaged var hashtag: NSArray?
static func parseClassName() -> String {
return "Post"
}
override class func query() -> PFQuery<PFObject>? {
let query = PFQuery(className: Post.parseClassName())
return query
}
}
extension Post: FeedCellSupport {
var username:String?{
return postUser
}
var userImg:PFFile?{
return postUserImg
}
var commentText:String?{
guard let commentTxt = comment else {
return ""
}
return commentTxt
}
TableView.swift
protocol FeedCellSupport {
var postDate: String? { get }
var postId: String? { get }
var postObj: PFObject? { get }
var img: PFFile? { get }
var username:String?{ get }
var userImg:PFFile?{ get }
var commentText:String?{ get }
var commentFrom:String?{ get }
var postText: String? { get }
var likes: Int? { get }
var isLiked: Bool? { get }
var isSell: Bool? { get }
var hashtags: NSArray? { get }
var mentions: NSArray? { get }
}
func fetchObjects() {
let postQuery = Post.query()?
.whereKeyExists("objectId")
.includeKeys(["postBy", "commentPointer", "commentPointer.commentBy", "commentBy"])
.order(byDescending: "createdAt")
as! PFQuery<Post>
postQuery.limit = self.page
postQuery.cachePolicy = .networkElseCache
postQuery.findObjectsInBackground { (object:[Post]?, error:Error?) in
if error == nil {
self.results = object!
}else {
print(error?.localizedDescription as Any)
}
self.tableView.reloadData()
self.refresher.endRefreshing()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ShopDetailCell", for: indexPath) as! ShopDetailCell
if let object = results[indexPath.row] as? FeedCellSupport{
cell.postID = object.postObj!
cell.userNameLabel.text = object.username!
cell.userNameLabel.sizeToFit()
cell.descriptionLabel.text = object.postText!
cell.descriptionLabel.sizeToFit()
cell.commentByLabel.text = object.commentFrom!
cell.commentByLabel.sizeToFit()
cell.commentLabel.text = object.commentText!
cell.commentLabel.sizeToFit()
cell.delegate = self
}
return cell
}
How can I ensure that data is updated only when it changes?

What are your App functionalities? Your problem is associated with the way suitable to accomplish your task, that is: to update app contents.
So it is about business logic: when should the information be updated?
The solution to your problem is using user toggled events to refresh the contents or other possible solutions such as Application Delegate.
Except for these answers, the contents cannot update tableview automatically as your expectation.
Update
I considered this line of code is lagging:
if let object = results[indexPath.row] as? FeedCellSupport{
//other code
}
Because code inside these braces is dealing with data setting. You could try to mock up local data instead of this results array to say whether this is the problem.

Related

Filtration coredata model in tableView

I have two models in my Coredata like:
CountryCD {
#NSManaged public var id: Int16
#NSManaged public var title: String?
#NSManaged public var cities: NSSet?
}
and
CityCD {
#NSManaged public var id: Int16
#NSManaged public var title: String?
#NSManaged public var country: CountryCD?
}
and showing it in tableView with some quantity of sections and rows
country[section].cities[indexPath.row]
I'm trying to add filtration in tableView
code for this right now is:
*
let filtered = countries
.compactMap { (($0.cities as? Set<CityCD>)?
.filter { $0.title?.lowercased()
.contains(text.lowercased()) ?? false } ?? []) }
.filter { !$0.isEmpty }
and it work almost how I need, it gives me [[CityCD]], but I need [CountryCD] to show filtration result in my tableView.
Can someone help me to figure out how to fix this?
source Swift Filter Nested Array
If anyone will face problem aka I did, here is solution I reached with help of #Joakim Danielson clue
in model:
var countriesDict: [CountryCD: Set<CityCD>]
var filteredCountriesDict: [CountryCD: Set<CityCD>]
when populate:
countryDict = savedCountries.reduce([CountryCD: Set<CityCD>]()) { [$1: $1.cities as? Set<CityCD> ?? [] ] }
filteredCountryDict = [:]
tableView.reloadData()
in numberOfSections:
return isFiltering ? filteredCountryDict.count : countryDict.count
in numberOfRowsInSection:
return isFiltering ? Array(filteredCountryDict)[section].value.count :
Array(countryDict)[section].value.count
in cellForRowAt:
let dictionary = .isFiltering ?
Array(filteredCountryDict)[indexPath.section] :
Array(countryDict)[indexPath.section]
let cities = (dictionary as NSSet).allObjects as? [CityCD]
cell.textLabel?.text = cities?[indexPath.row].title
when filtering:
let text = searchingText?.lowercased()
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
isFiltering = !(text == "")
if isFiltering {
countryDict.forEach { (key, value) in
self.filteredCountryDict[key] = value.filter({$0.title?.lowercased().contains(text) ?? false})
}
}else{
filteredCountryDict.removeAll()
}
tableView.reloadData()
hoping would be useful

Parse LiveQuery How do I uses Loadmore , limits

Hi I'm uses ParseLiveQuery and PFSubclassing in Swift 3
My question is limits is not working for me.
Here is my PFSubclassing
import Foundation
import Parse
class Post: PFObject, PFSubclassing {
#NSManaged var postBy: PFUser?
#NSManaged var postText: String?
#NSManaged var postImg: PFFile?
#NSManaged var sell: NSNumber?
#NSManaged var commentPointer: PFObject?
#NSManaged var commentBy: PFUser?
#NSManaged var comment: String?
#NSManaged var likeCount: NSNumber?
#NSManaged var mention: NSArray?
#NSManaged var hashtag: NSArray?
static func parseClassName() -> String {
return "Post"
}
override class func query() -> PFQuery<PFObject>? {
let query = PFQuery(className: Post.parseClassName())
query.whereKeyExists("objectId")
query.includeKeys(["postBy", "commentPointer", "commentPointer.commentBy", "commentBy"])
query.order(byDescending: "createdAt")
query.cachePolicy = .networkElseCache
return query
}
}
And It is my tableVieController
class FeedVC: UITableViewController, ShopDetailDelegate {
private var subscription: Subscription<Post>!
//Add friends and me
var postLiveQuery: PFQuery<Post> {
return Post.query() as! PFQuery<Post>
}
var client: Client {
return ParseLiveQuery.Client.shared
}
var results = [Post]()
I had declare subscription in ViewDidLoad()
subscription = client.subscribe(postLiveQuery).handleSubscribe { [weak self] (_) in
// Fetch the objects on subscription to not miss any
self?.fetchObjects()
}.handleEvent { [weak self] (_, event) in
self?.handleEvent(event: event)
}
And Here is what I'm trying to this.
func fetchObjects() {
postLiveQuery.limit = self.page
postLiveQuery.findObjectsInBackground().continue(with: BFExecutor.mainThread(), with: { (task) -> Any? in
guard let objects = task.result as? [Post] else {
return nil
}
self.results = objects
self.tableView.reloadData()
print("Got it!!!")
print(objects)
return nil
})
}
//load more
func morefetchObjects() {
if page <= results.count {
//Start animating indicator
indicator.startAnimating()
//increase page size to load + 10 posts
page = page + 1
postLiveQuery.limit = self.page
postLiveQuery.findObjectsInBackground().continue(with: BFExecutor.mainThread(), with: { (task) -> Any? in
guard let objects = task.result as? [Post] else {
return nil
}
self.results = objects
self.tableView.reloadData()
print("morefetchObjects")
print(objects)
return nil
})
}
}
//scrolled down
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y >= scrollView.contentSize.height - self.view.frame.size.height * 2 {
morefetchObjects()
}
}
It works fine but When I called fetchObject() then It retrieve all of datas from parse-server. I want fetch only limits and then When I scroll down I wish to get retrieve more data.
How can I implement this?

UITableview crashes when scrolling up in Swift

I am having this crash when trying to scroll up my tableview. My array is not nil. Why does it crash every time I try to scroll up? I am trying to display data from Core Data.
Here is my code:
var product = [NSManagedObject]()
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return product.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cellIdentifier = "CheckOutTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CheckOutTableViewCell
let item = product[indexPath.row]
** It crashes here, every time I try to scroll up my table view
cell.productTitle.text = item.valueForKey("name") as! String
cell.productDescription.text = item.valueForKey("size") as! String
return cell
}
func fetch() {
let moc = DataController().managedObjectContext
let productFetch = NSFetchRequest(entityName: "Product")
do {
let fetchedResults: [NSManagedObject] = try moc.executeFetchRequest(productFetch) as! [Product]
if let results: [NSManagedObject] = fetchedResults {
product = results
print("results:\(results.count)")
}
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
}
Product.swift
import Foundation
import CoreData
class Product: NSManagedObject {
}
Product+CoreDataProperties.swift
import Foundation
import CoreData
extension Product {
#NSManaged var id: String?
#NSManaged var name: String?
#NSManaged var img: String?
#NSManaged var quantity: String?
#NSManaged var size: String?
#NSManaged var price: String?
#NSManaged var promo: String?
}
Crash Screenshot
You should check the size of the array before getting your product, and use optional binding (if let) to check that the value exists in the dictionary:
if indexPath.row < product.count {
let item = product[indexPath.row]
if let name = item.valueForKey("name") as? String {
cell.productTitle.text = name
}
if let size = item.valueForKey("size") as? String {
cell.productDescription.text = size
}
}
To write safer code, it's a good idea to avoid force unwrapping when possible.
In this case, given that you have a Product class for your model, you should store your products as [Product], you can then avoid using valueForKey:
if indexPath.row < product.count {
let item = product[indexPath.row]
if let name = item.name as? String {
cell.productTitle.text = name
}
if let size = item.size as? String {
cell.productDescription.text = size
}
}

CoreData and Swift: contents gets swapped

I am new to swift and have a problem with CoreData: I can save and load to and from my CoreData store. But when I load the saved data from CoreData the data has been rearranged. I don't want this to happen. How do I solve this?
My code is this:
Meetings.swift
import Foundation
import CoreData
class Meetings: NSManagedObject {
#NSManaged var meetingTimeLeft: NSNumber
#NSManaged var title: String
#NSManaged var date: NSDate
#NSManaged var timeFrom: NSDate
#NSManaged var timeTo: NSDate
#NSManaged var timeFromActual: NSDate
#NSManaged var timeToActual: NSDate
#NSManaged var location: String
#NSManaged var locationRoom: String
#NSManaged var meetingGoals: String
#NSManaged var purpose: String
#NSManaged var averageHourlyCost: NSNumber
#NSManaged var completed: NSNumber
#NSManaged var durationActual: NSNumber
#NSManaged var durationPlanned: NSNumber
#NSManaged var persons: NSSet
#NSManaged var agendas: NSSet
#NSManaged var minutes: NSSet
#NSManaged var relationship: NSManagedObject
#NSManaged var startSetting: StartSetting?
}
extension Meetings {
func addPer(value: Person) {
self.mutableSetValueForKey("persons").addObject(value)
}
func removePer(value: Person) {
self.mutableSetValueForKey("persons").removeObject(Person)
//self.mutableSetValueForKey("persons").addObject(value)
}
func getPer() -> [Person] {
var persons: [Person]
persons = self.persons.allObjects as! [Person]
return persons
}
func addAgenda(value: AgendaItem) {
self.mutableSetValueForKey("agendas").addObject(value)
}
func removeAgenda(value: AgendaItem) {
self.mutableSetValueForKey("agendas").removeObject(AgendaItem)
//self.mutableSetValueForKey("persons").addObject(value)
}
func getAgenda() -> [AgendaItem] {
var agendas: [AgendaItem]
agendas = self.agendas.allObjects as! [AgendaItem]
return agendas
}
func getAgendaItem(i: Int) -> AgendaItem {
var agendas: [AgendaItem]
agendas = self.agendas.allObjects as! [AgendaItem]
let agendaItem = agendas[i]
return agendaItem
}
}
person.swift
import Foundation
import CoreData
class Person: NSManagedObject {
#NSManaged var name: String
#NSManaged var email: String
#NSManaged var phone: NSNumber
#NSManaged var title: String
#NSManaged var company: String
#NSManaged var photo: NSData
#NSManaged var meeting: Meetings
}
AgendaItem.swift
import Foundation
import CoreData
class AgendaItem: NSManagedObject {
#NSManaged var agendaItemTitle: String
#NSManaged var presenter: String
#NSManaged var durationPlanned: NSNumber
#NSManaged var durationActual: NSNumber
#NSManaged var filePresentation: NSData
#NSManaged var fileSupporting: NSData
#NSManaged var agendaTimeStart: NSDate
#NSManaged var meeting: Meetings
}
when saving i do like this:
internal var meetingsModels = [Meetings]()
public let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
managedObjectContext?.deleteObject(meetingsModels[meetingsIndexPassed.row]) meetingsModels.removeAtIndex(meetingsIndexPassed.row)
let entityDescription = NSEntityDescription.entityForName("Meetings", inManagedObjectContext: managedObjectContext!)
let entityDescription2 = NSEntityDescription.entityForName("Person", inManagedObjectContext: managedObjectContext!)
let entityDescription3 = NSEntityDescription.entityForName("AgendaItem", inManagedObjectContext: managedObjectContext!)
let meetings = Meetings(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext)
meetings.title = meetingTitle_txt.text!
meetings.date = lDate
meetings.timeFrom = lStartTime
meetings.timeTo = lEndTime
meetings.location = address_txt.text!
meetings.purpose = details[0]
meetings.meetingGoals = details[1]
meetings.locationRoom = details[2]
meetings.durationPlanned = meetingsEditPassed.durationPlanned
meetings.completed = false
for var i = 0; i < personsEdit.count; i++ {
let person = Person(entity: entityDescription2!, insertIntoManagedObjectContext: managedObjectContext)
person.name = personsEdit[i].name
person.email = personsEdit[i].email
person.title = personsEdit[i].title
person.company = personsEdit[i].company
person.phone = personsEdit[i].phone
meetings.addPer(person)
}
for var i = 0; i < agendaEdit.count; i++ {
let agenda = AgendaItem(entity: entityDescription3!, insertIntoManagedObjectContext: managedObjectContext)
agenda.agendaItemTitle = agendaEdit[i].agendaItemTitle
//print(agendaEdit[i].agendaItemTitle)
agenda.presenter = agendaEdit[i].presenter
agenda.durationPlanned = agendaEdit[i].durationPlanned
meetings.addAgenda(agenda)
}
do {
try managedObjectContext?.save()
} catch let error1 as NSError {
print("\(error1)")
}
meetingsModels.append(meetings)
when loading i do this:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//1
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
//2
let fetchRequest = NSFetchRequest(entityName:"Meetings")
//fetchRequest.sortDescriptors = nil
//3
let fetchedResults: [Meetings]
do {
try
fetchedResults = (managedContext.executeFetchRequest(fetchRequest) as? [Meetings])!
if let results: [Meetings] = fetchedResults {
var temp = [Meetings]()
var temp2 = [Meetings]()
for tmp: Meetings in results {
temp.append(tmp)
if tmp.completed.boolValue {
temp2.append(tmp)
}else {
}
}
meetingsModels = temp
meetingsModels2 = temp2
} else {
}
} catch let error1 as NSError {
print("could not fetch meetings: \(error1), \(error1.userInfo)")
}
}
i hope that some of you can help me.
Your meetings data is being "rearranged" just because your Meetings don't belong to any ordered relationship. I'm not able to run your code on my side. However, I can recommend you one of the following:
Use ordered relations any time you want to make sure the order of
data is preserved any time you fetch it.
Use the
sortDescriptors property on your fetch requests. When you
create one, you can add a sort descriptor to it in order to
arrange data.

NSSet and Core Data

I am trying to save two lists of objects in core data. I have a protocol method which adds my tags as (Tag) and puts them into an array of tags. I am not able to save these objects as I am using an NSSet and the extension (for swift) as:
extension CurrentTransaction {
func addTagObject(value:Tag) {
var items = self.mutableSetValueForKey("tagMembers");
items.addObject(value)
}
func removeTagObject(value:Tag) {
var items = self.mutableSetValueForKey("tagMembers");
items.removeObject(value)
}
}
The two objects from core data I am trying to save are:
class Pic: NSManagedObject {
#NSManaged var picName: String!
#NSManaged var picNumber: String!
#NSManaged var currentTransaction: CurrentTransaction
class func createInManagedObjectContext(moc: NSManagedObjectContext, name: String, number: String) -> Pic {
let newPic = NSEntityDescription.insertNewObjectForEntityForName("Pic", inManagedObjectContext: moc) as! Pic
newPic.picName = name
newPic.picNumber = number
return newPic
}
}
class Tag:
class Tag: NSManagedObject {
#NSManaged var tagName: String!
#NSManaged var tagNumber: String!
#NSManaged var currentTransaction: CurrentTransaction
class func createInManagedObjectContext(moc: NSManagedObjectContext, name: String, number: String) -> Tag {
let newTag = NSEntityDescription.insertNewObjectForEntityForName("Tag", inManagedObjectContext: moc) as! Tag
newTag.tagName = name
newTag.tagNumber = number
return newTag
}
}
class currentTransaction:
class CurrentTransaction: NSManagedObject {
#NSManaged var destinationPIC: String
#NSManaged var sourcePIC: String
#NSManaged var nvdNumber: String
#NSManaged var numberOfAnimals: NSNumber
#NSManaged var requestType: String
#NSManaged var date: NSDate
#NSManaged var tagMembers: NSSet
#NSManaged var picMembers: NSSet?
class func createInManagedObjectContext(moc: NSManagedObjectContext, toPic: String, fromPic: String, nvd: String, numAnimals: NSNumber, request: String, date: NSDate, tags: NSSet, pics: NSSet) -> CurrentTransaction {
let newTransaction = NSEntityDescription.insertNewObjectForEntityForName("CurrentTransaction", inManagedObjectContext: moc) as! CurrentTransaction
newTransaction.destinationPIC = toPic
newTransaction.sourcePIC = fromPic
newTransaction.nvdNumber = nvd
newTransaction.numberOfAnimals = numAnimals
newTransaction.requestType = request
newTransaction.tagMembers = tags
newTransaction.picMembers = pics
newTransaction.date = date
return newTransaction
}
}
When I call the CurrentTransaction.addTagObject(tag:Tag) I get "Cannot invoke 'addTagObject' with an argument list of type '(Tag)'
Here is my addTags protocol method:
func addTags(tag: Tag) {
//println("IN ADD TAGS")
//println(tag.tagNumber)
self.tagList.append(tag)
CurrentTransaction.addTagObject(tag) //Error on this line
}
Reason: addTagObject() is not defined as class function and cannot be invoked on class CurrentTransaction. If you intended to use it as class fucntion, change it to:
class func addTagObject(value:Tag) {
var items = self.mutableSetValueForKey("tagMembers");
items.addObject(value)
}