Ordering by object.count in PFQueryTableViewController - swift

I need to query all objects in a class and order them all by the frequency of each object in Swift. I'm getting the query using...
// Define the query that will provide the data for the table view
override func queryForTable() -> PFQuery{
var query = PFQuery(className: "Upvotes")
return query
}
...but can't seem to retrieve an objects.count item since I can't use an async completion block with a findObjectsInBackground call. Is this something I should handle in the cellForRowAtIndexPath()?

But you can do something like this:
class demo: UIViewController {
var parseObjects: NSArray!
override func viewDidLoad() {
}
// Define the query that will provide the data for the table view
func yourQuery() -> PFQuery {
var query = PFQuery(className: "Upvotes")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if (objects!.count > 0) {
parseObjects = objects //Store those objects in your variable
}
} else {
// Log details of the failure
println("Error: \(error) \(error!.userInfo!)")
}
}
}
}
and after that in your tableViewDelegate use parseObjects variable...

Related

Accessing Firestore data outside of Function [duplicate]

This question already has an answer here:
Assign value of a Firestore document to a variable
(1 answer)
Closed 3 years ago.
I have a FireStore function in my FirestoreService file as below;
func retrieveDiscounts() -> [Discount] {
var discounts = [Discount]()
reference(to: .discounts).getDocuments { (snapshots, error) in
if error != nil {
print(error as Any)
return
} else {
guard let snapshot = snapshots else { return }
discounts = snapshot.documents.compactMap({Discount(dictionary: $0.data())})
}
}
return discounts
}
how do I get returned values to populate my private var discounts = [Discount]() variable in my viewController
Many thanks as always...
Your functions will get your UI to freeze until its operation is complete. The function which may take long duration to complete should be done asyncronous using escaping closures. The function should be like below :
func retrieveDiscounts(success: #escaping([Discount]) -> ()) {
var discounts = [Discount]()
reference(to: .discounts).getDocuments { (snapshots, error) in
if error != nil {
print(error as Any)
success([])
return
} else {
guard let snapshot = snapshots else { return }
discounts = snapshot.documents.compactMap({Discount(dictionary: $0.data())})
success(discounts)
}
}
}
Note: The data returns empty if error. Please handle error case if you need.
We first need an instance of FirestoreService class. Then the instance should call the retrieveDiscounts() function and populate it to our instance i.e. discounts.
Code:
class ViewController: UIViewController {
private var discounts = [Discount]() {
didSet {
self.tableView.reloadData()
}
}
func viewDidLoad() {
super.viewDidLoad()
FirestoreService().retrieveDiscounts { discounts in
self.discounts = discounts
}
}
}

How do I call a function only if another function is complete? (Swift and retrieving from Parse)

I am trying to only call a function only if I have retrieved a certain PFObjectfrom my Parse backend in a separate function. At the moment I am calling this second function after a set delay of 3.0 and it is working, but only if the first query function is called within 3.0, otherwise I have to pullToRefresh the tableView after the first function is eventually finished for it to populate with the Parse data. (I am using a PFQueryTableViewController by the way)
Here is my code at the moment (I am aware queryForTable is an override so is being called regardless, so is there a way to change this to a normal func?) :
override func viewDidLoad() {
// ideally want something like "if getX'IsComplete' then queryForTable() and self.loadObjects()"
retrieveFirstX()
let delay = 3.0 * Double(NSEC_PER_SEC) // retrieveFirstX must load within 3.0 for table to populate without needing to pullToRefresh
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), {
self.queryForTable()
self.loadObjects()
})
}
var X = PFObject(className: "X")
func retrieveFirstX() -> Bool {
let xQuery = PFQuery(className: "X")
xQuery.orderByDescending("createdAt")
xQuery.getFirstObjectInBackgroundWithBlock {
(object: PFObject?, error: NSError?) -> Void in
if error == nil {
self.X = object!
}
}
return true
}
override func queryForTable() -> PFQuery {
let xRelation = X.relationForKey("xPosts") as PFRelation!
let relationQuery = xRelation.query()
let xPostsQuery = PFQuery(className: "Posts")
xPostsQuery.includeKey("postUser")
xPostsQuery.whereKey("objectId", matchesKey: "objectId", inQuery: relationQuery)
xPostsQuery.cachePolicy = .NetworkElseCache
xPostsQuery.orderByDescending("createdAt")
return xPostsQuery
}
Do I need to use completion handlers, and if so how do I do that as I have never used them before?
Thanks in advance!
A completion handler sounds right, something like:
override func viewDidLoad() {
let completionHandler = {
self.queryForTable()
self.loadObjects()
}
retrieveFirstX(completionHandler)
}
func retrieveFirstX(completion: ()->()) -> Bool {
let xQuery = PFQuery(className: "X")
xQuery.orderByDescending("createdAt")
xQuery.getFirstObjectInBackgroundWithBlock {
(object: PFObject?, error: NSError?) -> Void in
if error == nil {
self.X = object!
completion()
}
}
return true
}

Run code after a background process - parse.com

I have a findObjectsInBackgroundWithBlock method in my viewController. Now i want to execute code, but just until this background method is finished. How can i do that?
I'm using the swift programming lanuage.
Here is some example code that could help you. It is not clear how you would restrict the code (PRE-BACKGROUND CODE) to run only while the background processing has completed. You may want to insert some code in the notification response function either to confirm that that PRE-BACKGROUND CODE is completed or to terminate it.
// ParseTestViewController.swift
import UIKit
import Foundation
import Parse
class ParseTestViewController: UIViewController {
var userData = [String]()
func codeToExecuteBeforeStringsAreAppended() {
}
func codeToExecuteAfterStringsAreAppended() {
// can use the array 'userData'
}
override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "notificationResponse:",
name: "recordsLoaded",
object: nil
)
self.getUserdataForUsername("MyName")
/* ==========================
Insert code tto be executed immediately after making the call that must not use the data returned by Parse. The function returns right away and the background may not have completed.
*/
codeToExecuteBeforeStringsAreAppended()
}
func getUserdataForUsername (queryUserName: String) {
var query = PFQuery(className:"UserData")
query.whereKey("username", equalTo: queryUserName)
let notification = NSNotification(name: "userDataRetrieved", object: self)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
for object in objects! {
if let username = object["username"] as? String {
self.userData.append (username)
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
NSNotificationCenter.defaultCenter().postNotification(notification)
}
}
func notificationResponse (notification: NSNotification) {
// this is executed after the background processing is done
// Insert the code that uses the data retrieved from Parse
codeToExecuteAfterStringsAreAppended()
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
This is pretty well covered in the documentation and guide on parse.com. But maybe you have a more specific question/scenario?
Guide to queries on parse.com
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.
println("Successfully retrieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
println(object.objectId)
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
Edit: specific version for PFUser to array of usernames
var usernames: [String]?
func loadUsernames () {
if let query = PFUser.query() {
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil { // No error - should be good to go
self.userNames = (objects as! [PFUser]).map({$0.username!})
// Call/run any code you like here - remember 'self' keyword
// It will not run until userNames is populated
self.callSomeMethod()
} else { // Error - do something clever
}
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
loadUsernames()
}

How do I share variables inside a singleton class

I am trying to create a common class for storing and retrieving data in Parse. I made the ParseProcessing class a singleton class. From my main View Controller I load the data and store it into a dictionary in the ParseProcessing. I do this by creating a shared instance of the ParseProcessing class. From another view controller I try to access the data from the dictionary. I assumed that because ParseProcessing is a singleton class that I have a single copy of the dictionary. This does not appear to be correct. How should I declare the variables inside the ParseProcessing so that they are shared? The code is shown below:
import UIKit
var gSep = ","
class QwikFileViewController: UIViewController {
var loadData = ParseProcessing.sharedInstance
override func viewDidLoad() {
super.viewDidLoad()
// load data from Parse
loadData.loadCategorySubcategoryData()
loadData.loadRecordsFromParse()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
ParseProcessing Singleton Class
import UIKit
import Parse
class ParseProcessing: Parse {
var dictMenuList = [String:String]()
var noteTitle = [String]()
var notes = [String]()
var thumbnailFiles = [PFFile]()
var objectIds = [String]()
var noteImage = UIImage()
class var sharedInstance:ParseProcessing {
struct singleton {
static let instance:ParseProcessing = ParseProcessing()
}
return singleton.instance
}
// Load Category/Subcategory data from Parse Data Base
func loadRecordsFromParse () -> Bool{
var tmpFile = [PFFile]()
var loadComplete = false
var query = PFQuery(className:"Record")
query.findObjectsInBackgroundWithBlock {
(objects, error) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) items.")
for object in objects! {
self.noteTitle.append(object["title"] as! String)
self.notes.append(object["notes"] as! String)
self.thumbnailFiles.append(object["thumbnail"] as! PFFile)
self.objectIds.append(String(stringInterpolationSegment: object.objectId))
}
} else {
println("\(error)")
}
loadComplete = true
}
return loadComplete
}
// Load Category/Subcategory data from Parse Data Base
func loadCategorySubcategoryData () // -> Dictionary <String,String>
{
var success : Bool = false
var d : Dictionary <String,String> = ["":""]
var menu = PFQuery(className: "Classification")
println("ParseProcessing: loadCategory...")
menu.findObjectsInBackgroundWithBlock {
(objects, error) -> Void in
if error == nil {
var category = ""
var subcategory = ""
for object in objects! {
category = object["category"] as! String
println("ParseProcessing: category = \(category)")
subcategory = object["subcategory"] as! String
println("ParseProcessing: subcategory = \(subcategory)")
d[category] = subcategory
}
success = true
self.dictMenuList = d
return
} else {
println("ParseProcessing: error = \(error)")
success = false
}
}
return
}
}
Another View Controller to examine the data
import UIKit
class TestViewController: UIViewController {
var dictMenuList = [String:String]()
var loadData = ParseProcessing.sharedInstance
override func viewDidLoad() {
super.viewDidLoad()
dictMenuList = loadData.dictMenuList
println("dictMenuList: \(dictMenuList)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
The problem is that findObjectsInBackgroundWithBlock is asynchronous method (i.e. it returns immediately but the closure is called later when the query is done). So you cannot return loadComplete in loadRecordsFromParse, for example. This background request will almost certainly never be done by the time loadRecordsFromParse returns.
Instead, you probably want to adopt the completionHandler pattern. For example, this sample loadRecords doesn't try to return anything immediately, but rather will call the completionHandler when the request is done.
func loadRecords(completionHandler:([SomeObject]?, NSError?) -> ()) {
let query = PFQuery(className: "SomeClass")
query.findObjectsInBackgroundWithBlock { objects, error in
// build some model object
completionHandler(objectArray, error)
}
}
And you'd call it like so:
loadData.loadRecords() { objects, error in
// use `objects` (and make sure `error` is `nil`) here
}
// but do not use those variables here, as the above closure probably has not run yet!
Frankly, I'd be inclined to get rid of those properties in your singleton altogether. When you're dealing with asynchronous code, to have public properties that are updated asynchronously is going to be a source of heartache. You can do it, but it wouldn't be my first choice.
For example, when TestViewController is presented, you cannot assume that the asynchronous fetch associated with dictMenuList is done yet. I look at this and wonder if it makes sense for TestViewController to initiate the fetch itself and then use dictMenuList in the completion handler. That's going to be easiest.
If you must initiate the asynchronous request from one view controller and then have another view controller be informed when that asynchronous request is done, then you might have to use some other pattern, such as notifications (e.g. use NSNotificationCenter, and have the singleton post notifications when the various requests are done, and then any view controller that needs to be informed of this fact can add themselves as observers for that notification).

Parse.com Tried to save an object with a new, unsaved child with findObjectsInBackgroundWithBlock in Swift

Here are the definitions of entities:
class Event : PFObject, PFSubclassing {
override class func load() {
superclass()?.load()
self.registerSubclass()
}
class func parseClassName() -> String! {
return "Event"
}
}
˚
Now I am trying to retrieve list of teams belongs to Event:
var teams: [Team] = []
var query = PFQuery(className: "Team")
query.includeKey("event")
if (event != nil) {
query.whereKey("event", equalTo: event)
}
query.cachePolicy = kPFCachePolicyCacheThenNetwork
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error:NSError!) -> Void in
I am getting error as,
[Error]: Caught "NSInternalInconsistencyException" with reason "Tried to save an object with a new, unsaved child.":
But if I remove the whereKey statement: query.whereKey("event", equalTo: event), the error disappears.
Any help would be appreciated.