Swift/Parse - No Results Matched the Query when Updating Data - ios

I have recently been playing around with parse,and yes, I know that it is closing soon:(. I have this error however that keeps on coming up. I am trying to update my textview.text to parse every single second. However, when the code runs a message comes up saying "no results matched the query", even though textview.text is not empty. I would really appreciate your help. Thanks.
override func viewDidLoad() {
super.viewDidLoad()
notes["Content"] = detailDescriptionLabel.text
notes.saveInBackgroundWithBlock { (succes, error) -> Void in
if error != nil {
print("unable to save objects")
}
}
scheduledTimerWithTimeInterval()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
}
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function **Countdown** with the interval of 1 seconds
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateText"), userInfo: nil, repeats: true)
}
func updateText () {
let query = PFQuery(className: "Notes")
query.getObjectInBackgroundWithId("Content") { (notes, error) -> Void in
notes?["Content"] = self.detailDescriptionLabel.text
print("Updated")
}
}
Picture of error in Log
Image of Parse Dashboard

Related

Looking for best practices when updating a tableview by monitoring for changes in DB

I'm somewhat new to this and this is my first question on stackoverflow. Thanks in advance for your help and bear with me if my formatting sucks
I've got multiple views within my app (all displaying data using tableview subviews) that need to update automatically when the data changes on the database (Firestore), i.e. another user updates the data.
I've found a way to do this which is working well, but I want to ask the community if there's a better way.
Currently, I am creating a Timer object with a timeInterval of 2. On the interval, the timer queries the database and checks a stored data sample against updated data. If the two values vary, I run viewDidLoad which contains my original query, tableView.reloadData(), etc..
Any suggestions or affirmations would be very useful.
var timer = Timer()
var oldChallengesArray = [String]()
var newChallengesArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
//set tableview delegate
mainTableView.delegate = self
mainTableView.dataSource = self
//set challengesmodel delegate
challengesModel.delegate = self
//get challenges
DispatchQueue.main.async {
self.challengesModel.getChallenges(accepted: true, challengeDenied: false, incomingChallenges: false, matchOver: false)
self.mainTableView.reloadData()
}
scheduledTimerWithTimeInterval()
}
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function "updateCounting" with the interval of 1 seconds
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(self.updateTableView), userInfo: nil, repeats: true)
}
#objc func updateTableView(){
ChallengeService.getAllUserChallengeIDs(accepted: true, challengeDenied: false, matchOver: false) { (array) in
if array.isEmpty {
return
} else {
self.newChallengesArray = array
if self.oldChallengesArray != self.newChallengesArray {
self.oldChallengesArray = self.newChallengesArray
self.newChallengesArray.removeAll()
self.viewDidLoad()
}
}
}
}
Firestore is a "realtime database", that means that the database warns you when changes happen to the data. To achieve that the app needs to subscribe to relevant changes in the db. The sample code below can be found here:
db.collection("cities").document("SF")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
print("Current data: \(data)")
}
Also, I would like to point out that calling viewDidLoad is incorrect, you should never call viewDidLoad yourself, create an func to update the data. Something like this:
DispatchQueue.main.async {
self.mainTableView.reloadData()
}

Handle thousands of socket message in iOS

I am working on a code where I need to display socket messages in the tableview and tableview get scroll to bottom with slow animation. I handle that but if the message load from socket is continues (consider 20/30 message per second) then the UI get freeze. I need to show the message like Facebook do on live screen one or two messages per iteration.
Here is my code
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(timeInterval: 0.025, target: self, selector: #selector(self.scrollTableView(_:)), userInfo: nil, repeats: true)
self.designLayout()
}
#objc func scrollTableView(_ timer: Timer) {
guard messagesData.count > 0 else {
return
}
if tableView.contentSize.height > tableView.bounds.height {
tableView.contentInset.top = 0
}
tableView.scrollToRow(at: IndexPath(row: messagesData.count - 1, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
}
self.socket.on(“key”) {data, ack in
print("data type is \(type(of: data))")
let arrayValue = data as Array<Any>
DispatchQueue.global(qos: .background).async {
self.handleCountsAndMessaging(data: arrayValue)
}
}
func handleCountsAndMessaging(data: Array<Any>) {
if let arrData = data[0] as? NSMutableDictionary {
if(arrData.object(forKey: "text") != nil) {
self.tableDataLoading(str: String(describing: arrData.object(forKey: "text")!))
}
}
}
func tableDataLoading() {
print ("sttttttt \(str)")
DispatchQueue.main.async {
self.messagesData.add(str)
self.tableView.reloadData()
}
}
Some times I get messages like 200 at time from socket and again 200 while processing previous messages, CPU consumption is showing like 120 and UI get freeze.
Thanks in advance.
Well, there is 'live' and 'live':)
This a phone we are talking about, so it's useless to update the screen every 0.025 seconds... you can easily slow down your fetch time but let's work one problem at a time :
1/ Don't use tableView.scrollToRow, but rather 'insertRows(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation)' to insert a new row, this way if your user is reading, something, he won't be disturbed if you add 200 new item
https://developer.apple.com/documentation/uikit/uitableview/1614879-insertrows
2/ Slow down your UI updates, you are overusing your main thread... You can fetch every 25ms if you like, but I recommend using a throttling mechanism to update the UI only if you get new content, and only if last UI update wasn't less than 5 seconds ago.

How to call an NSTimer until selector is successful [duplicate]

This question already has answers here:
Stop NSTimer and dismiss view controller (swift)
(5 answers)
Closed 6 years ago.
I have a function called "Check" that checks if an object has been updated, if that's the case, the user is sent to another view controller. However, the NSTimer keeps repeating itself, I want it to stop after the user is sent to the other view controller.
func check(){
let current = PFUser.currentUser()?.objectForKey("username")
let check = PFQuery(className: "Requests")
check.whereKey("username", equalTo: current!)
check.whereKey("requestResponded", equalTo: "True")
check.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error != nil || objects == nil{
print("Request rejected.")
} else {
for object in objects!{
let service = object["service"] as! NSValue
print(service)
if service == 1{
self.performSegueWithIdentifier("detailedRequest", sender: self)
print("detailedRequest")
} else {
self.performSegueWithIdentifier("normalRequest", sender: self)
print("normal")
}
}
print("Successfully retrieved that object.")
}
})
}
self.timer = NSTimer.scheduledTimerWithTimeInterval(10.0, target: self, selector: #selector(self.check), userInfo: nil, repeats: true)
Declare a viewDidDisappear function and inside it invalidate your timer. This function automatically be called each time your viewController has disappeared.
Something like this:
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(true)
self.timer.invalidate()
}

Swift Language: Updating Parse objects

Good evening Coders!
I created a query that gets the username and location of people around me within 7miles of device location, I would like to update these objects every 5-10seconds(I haven't decided yet). What would be the best practice for this? should i create an NSTimer() and call it that way? please help!!
var timer = NSTimer()
override func viewDidLoad() {
super.viewDidLoad()
timer == NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "queryFunc", userInfo: nil, repeats: true)
}
func queryFunc() {
var query = PFQuery(className:"GameScore")
query.whereKey("playerName", equalTo:"Sean Plott")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
// The find succeeded.
NSLog("Successfully retrieved \(objects.count) scores.")
// Do something with the found objects
for object in objects { NSLog("%#", object.objectId)
}
} else {
// Log details of the failure
NSLog("Error: %# %#", error, error.userInfo!)
}
}
/*
This is just an example query, the point of this is the timer and how to update objects periodically
*/
}
try to add whereKey:nearGeoPoint:withinMiles: in your query which will find objects within 7 miles from your location
For further details for that check out this link
and when you found data after each query then remove all previous data and replace it with new data and refresh your view.
NSTimer is probably the Good Option go for it. Even I also have used timer to sync in background in every 5 min and it's working fine for me.
Hope this will help you.

Reloading CloudKit data when app comes to foreground

I'm trying to understand CloudKit, but I'm having an issue figuring out a real-world issue if I eventually turned it into an app.
I've got a tableviewcontroller that's populated by CloudKit, which works fine. I've got a detailviewcontroller that is pushed when you tap one of the entries in the tableview controller. The detailviewcontroller pulls additional data from CloudKit that wasn't in the initial tableviewcontroller. All of this works perfectly fine.
Now, the detailviewcontroller allows changing of an image and saving it back to a public database. That's working fine as well.
The scenario I'm dealing with is thinking about this core function being used in an app by multiple people. If one person uploads a photo (overwriting an existing photo for one of the records), what happens if another user currently has the app in the background? If they open the app, they'll see the last photo they saw on that page, and not the new photo. So I want to reload the data from CloudKit when the app comes back to the foreground, but I must be doing something wrong, because it's not working. Using the simulator and my actual phone, I can change a photo on one device and the other device (simulator or phone) doesn't update the data when it comes into the foreground.
Here's the code I'm using: First in ViewController.swift:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
println("viewDidLoad")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "activeAgain", name: UIApplicationWillEnterForegroundNotification, object: nil)
and then the activeAgain function:
func activeAgain() {
println("Active again")
fetchItems()
self.tblItems.reloadData()
println("Table reloaded")
}
and then the fetchItems function:
func fetchItems() {
let container = CKContainer.defaultContainer()
let publicDatabase = container.publicCloudDatabase
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Items", predicate: predicate)
publicDatabase.performQuery(query, inZoneWithID: nil) { (results, error) -> Void in
if error != nil {
println(error)
}
else {
println(results)
for result in results {
self.items.append(result as! CKRecord)
}
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.tblItems.reloadData()
self.tblItems.hidden = false
})
}
}
}
I tried adding a reload function to viewWillAppear, but that didn't really help:
override func viewWillAppear(animated: Bool) {
tblItems.reloadData()
}
Now here's the code in DetailViewController.swift:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "activeAgain", name: UIApplicationWillEnterForegroundNotification, object: nil)
self.imageScrollView.contentSize = CGSizeMake(1000, 1000)
self.imageScrollView.frame = CGRectMake(0, 0, 1000, 1000)
self.imageScrollView.minimumZoomScale = 1.0
self.pinBoardScrollView.maximumZoomScale = 8.0
showImage()
and then the showImage function:
func showImage() {
imageView.hidden = true
btnRemoveImage.hidden = true
viewWait.hidden = true
if let editedImage = editedImageRecord {
if let imageAsset: CKAsset = editedImage.valueForKey("image") as? CKAsset {
imageView.image = UIImage(contentsOfFile: imageAsset.fileURL.path!)
imageURL = imageAsset.fileURL
self.title = "Image"
imageView.hidden = false
btnRemoveImage.hidden = false
btnSelectPhoto.hidden = true
}
}
}
and the activeAgain function:
func activeAgain() {
showImage()
println("Active again")
}
Neither of these ViewControllers reload the data from CloudKit when they return. It's like it's cached the table data for the first ViewController and the image for the DetailViewController and it's using that instead of downloading the actual CloudKit data. I'm stumped, so I figure I better try this forum (my first post!). Hopefully I included enough required info to be useful.
Your showImage function is using a editedImage CKRecord. Did you also reload that record? Otherwise the Image field in that record would then still contain the old CKAsset.
Besides just reloading on a moment you feel right, you could also let the changes be pushed by creating a CloudKit subscription. Then your data will even change when it's active and changed by someone else.

Resources