I'm trying to run a PFQuery that will populate an array of custom structs.
Everything looks ok, but when I run the code the query returned is empty. I also tried this using PFUser.Query, which worked, but did not return a value for objectId, so tried to query the class instead.
Here is what I have:
var parseUsers:[ParseUsers] = []
var allUsers:[PFObject] = []
let query = PFQuery(className:"User")
let currentUser = PFUser.current()
query.whereKey("username", notEqualTo: currentUser?.username)
do {
allUsers = try (query.findObjects())
for i in allUsers{
var pImage = UIImage()
if i["profileImage"] != nil{
let imageFile = i["profileImage"] as? PFFileObject
imageFile?.getDataInBackground { (imageData: Data?, error: Error?) in
if let error = error {
print(error.localizedDescription)
} else if let imageData = imageData {
pImage = UIImage(data:imageData)!
}
}
}
let currentUserInfo = ParseUsers(objectID:i["objectId"] as! String,
username:i["objectId"] as! String,
userWorkouts:i["userWorkouts"] as! [Dictionary<String, String>],
profileImage:pImage)
parseUsers.append(currentUserInfo)
}
} catch {
print("Error")
}
Found out what the problem was!
For anyone experiencing similar issues in the future, when determining the class you want to query, you need to out _ in front. In the case above it would be let query = PFQuery(className:"_User").
Weird, as I couldn't see this mentioned in the parse documentation anywhere.
As mentioned by #William George in the Querying subsection of Users you can see how to construct a query on on the _User class.
A Below is an example of this (lifted straight from the docs):
var query = PFUser.query()
query.whereKey("gender", equalTo:"female")
var girls = query.findObjects()
You can query your Parse Server with PFUser just like with PFQuery but it can only be used on the _User class.
Related
I have two versions of code. Version 1 is the code I intend on using. I am trying to look up a current user's friends' in the friend array. For every friend, I want to append certain items to each array to display in a tableview cell. Version 1 incorporates a for in loop with i friends array contains: ["magellan"]. When I use a PFQuery to lookup data, it does not execute the query. However, version two is not run in a for in loop nor does it use friend. It uses the actual string "magellan" instead and works fine. What is my problem?. Thanks
Version 1:
override func viewdidload() {
for i in 0..<friendsArray.count {
friend = friendsArray[i]
print(friend)
let query2 = PFQuery(className: "CheckPost")
query2.whereKey("Username", equalTo: friend)
query2.findObjectsInBackground (block: { (objects:[PFObject]?, error: Error?) in
if let objects = objects {
for object in objects {
self.checkArray.append(object["Checks"] as! String)
self.checkArrayobjectId.append(object.objectId!);
self.checkTimeArray.append(object["UserTime"] as! String)
//append userimage
self.checkPicArray.append(UIImage(named: "randomguy")!)
print("successfully checked for friends checks")
}
} else if error != nil {
print(error)
}
})
}
Version 2:
override func viewdidload()
for i in 0..<friendsArray.count {
friend = friendsArray[i]
print(friend)
}
let query2 = PFQuery(className: "CheckPost")
query2.whereKey("Username", equalTo: "magellan")
query2.findObjectsInBackground (block: { (objects:[PFObject]?, error: Error?) in
if let objects = objects {
for object in objects {
self.checkArray.append(object["Checks"] as! String)
self.checkArrayobjectId.append(object.objectId!);
self.checkTimeArray.append(object["UserTime"] as! String)
//append userimage
self.checkPicArray.append(UIImage(named: "randomguy")!)
print("successfully checked for friends checks")
}
} else if error != nil {
print(error)
}
})
}
It looks like you are misunderstanding the async behavior of the findObjectInBackground function. This function is async and the callbacks will only be called after the loop has ended. You code should work with the find though. Something like this:
for i in 0..<friendsArray.count {
friend = friendsArray[i]
print(friend)
let query2 = PFQuery(className: "CheckPost")
query2.whereKey("Username", equalTo: friend)
let objects = query2.find()
if objects {
for object in objects {
self.checkArray.append(object["Checks"] as! String)
self.checkArrayobjectId.append(object.objectId!);
self.checkTimeArray.append(object["UserTime"] as! String)
//append userimage
self.checkPicArray.append(UIImage(named: "randomguy")!)
print("successfully checked for friends checks")
}
}
}
I large amount of data in my app with search functionality. I am using SQLite and Core Data to search and Fetch data.
Here is my search function,
func fetchSearchResultsWith(_ searchText : String?){
DispatchQueue.global(qos: .background).async {
var resArr : [Int64] = []
let stmt = "SELECT rowid FROM htmlText_fts WHERE htmlText MATCH '\(searchText!)*'"
do {
let res = try self.db.run(stmt)
for row in res {
resArr.append(row[0] as! Int64)
}
} catch {
print(error.localizedDescription)
}
let request : NSFetchRequest<Monos> = Monos.fetchRequest()
request.fetchLimit = 200
let predicate = NSPredicate(format: "id in %#", resArr)
request.predicate = predicate
var arr : [Items]? = []
do {
arr = try context.fetch(request)
} catch {
print(error.localizedDescription)
}
DispatchQueue.main.async(execute: {
self.monosSearchResult = arr
self.tableView.reloadData()
})
}
}
I am using DispatchQueue.global.async to avoid freezing UI, but then its returning async array and my table view ends up reloading with wrong result. If I use DispatchQueue.global.sync it works fine, but then my UI freezes when I type in to searchBar. I am not sure what I can do get right result. Any help will be appreciated!
Please let me know if you need any further information.
Since you have a 2 step search mechanism , a new search may be initiated before the other ones end , so to lightWeight this operation , store the last value of the textfield inside a var
lastSear = textfield.text
fetchSearchResultsWith(lastSear)
then do this inside the search function in 3 places
Before search the DB & after & before setting the array and reloading the table
if searchText != lastSear { return }
You have not included your table data source methods which populate the table, but I assume you are using values from self.monosSearchResult. If not, then your fetch code is populating the wrong values, and that may be part of your problem.
Additionally, your fetch request needs to be running on the appropriate thread for your NSManagedObjectContext, not necessarily (probably not) the global background queue. NSManagedObjectContext provides the perform and performAndWait methods for you to use their queues properly.
func fetchSearchResultsWith(_ searchText : String?){
// context: NSManagedObjectContext, presumably defined in this scope already
// since you use it below for the fetch.
// CHANGE THIS
// DispatchQueue.global(qos: .background).async {
// TO THIS
context.perform { // run block asynchronously on the context queue
var resArr : [Int64] = []
let stmt = "SELECT rowid FROM htmlText_fts WHERE htmlText MATCH '\(searchText!)*'"
do {
let res = try self.db.run(stmt)
for row in res {
resArr.append(row[0] as! Int64)
}
} catch {
print(error.localizedDescription)
}
let request : NSFetchRequest<Monos> = Monos.fetchRequest()
request.fetchLimit = 200
let predicate = NSPredicate(format: "id in %#", resArr)
request.predicate = predicate
var arr : [Items]? = []
do {
arr = try context.fetch(request)
} catch {
print(error.localizedDescription)
}
DispatchQueue.main.async(execute: {
self.monosSearchResult = arr
self.tableView.reloadData()
})
}
}
I have written the following function but I have a problem with its returning value.
In my console I can see the results pulled from AWS dynamoDB, I can even display it. As you can see, I am printing item variable.
I instantiated an array in my function and append each item pulled from AWS to it but it returning nil.
Can you please see what I miss in my code thanks.
func scanClientList(_ startFromBeginning: Bool) -> [Client]{
var clients = [Client]()
UIApplication.shared.isNetworkActivityIndicatorVisible = true
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
let queryExpression = AWSDynamoDBScanExpression()
//queryExpression.exclusiveStartKey = self.userId
queryExpression.limit = 20
dynamoDBObjectMapper.scan(Client.self, expression: queryExpression).continueWith(executor: AWSExecutor.mainThread(), block: { (task:AWSTask!) -> AnyObject! in
if let paginatedOutput = task.result {
for item in paginatedOutput.items as! [Client] {
print("ITEMS: \(item)")
clients.append(item)
}
if paginatedOutput.lastEvaluatedKey == nil {
}
}
UIApplication.shared.isNetworkActivityIndicatorVisible = false
if let error = task.error as? NSError {
print("Error: \(error)")
}
return nil
})
return clients
}
Actually my function here works perfectly, I didn't initialize the variable clients in the functions
I had something like
var clients = [Client]()?
and changed it to
var clients:Array<Client> = []
I am currently setting up CloudKit as a replacement to Parse and need to download all of my user records. I currently have around 600 records but I am only receiving 300.
I'm using a custom record zone called "User" rather than the default "Users" record zone as this app will only ever be tied to one appID.
The code I am using is based on the answer to the below question but it's not working for me. It seems that the query operation does not run when the cursor is nil as the print(userArray) is never called. Thanks in advance for your help!
CKQuery from private zone returns only first 100 CKRecords from in CloudKit
func queryAllUsers() {
let database = CKContainer.defaultContainer().privateCloudDatabase
let query = CKQuery(recordType: "User", predicate: NSPredicate(value: true))
let queryOperation = CKQueryOperation(query: query)
queryOperation.recordFetchedBlock = self.createUserObject
queryOperation.queryCompletionBlock = { cursor, error in
if cursor != nil {
print("there is more data to fetch")
let newOperation = CKQueryOperation(cursor: cursor!)
newOperation.recordFetchedBlock = self.createUserObject
newOperation.queryCompletionBlock = queryOperation.queryCompletionBlock
database.addOperation(newOperation)
} else {
print(userArray) //Never runs
}
}
database.addOperation(queryOperation)
}
func createUserObject(record: CKRecord) {
let name = record.objectForKey("Name") as! String!
let company = record.objectForKey("Company") as! String!
let dateInductionCompleted = record.objectForKey("DateInductionCompleted") as! NSDate!
var image = UIImage()
let imageAsset = record.objectForKey("Image") as! CKAsset!
if let url = imageAsset.fileURL as NSURL? {
let imageData = NSData(contentsOfURL:url)
let mainQueue = NSOperationQueue.mainQueue()
mainQueue.addOperationWithBlock() {
image = UIImage(data: imageData!)!
userArray.append(User(name: name, company: company, image: image, dateInductionCompleted: dateInductionCompleted))
}
}
print(userArray.count)
}
UPDATE
The question has been answered, it was possibly an inherent bug when using a cursor for large queries. The code now works by using a recursive function, working code below:
func queryRecords() {
let database = CKContainer.defaultContainer().privateCloudDatabase
let query = CKQuery(recordType: "User", predicate: NSPredicate(value: true))
let queryOperation = CKQueryOperation(query: query)
queryOperation.qualityOfService = .UserInitiated
queryOperation.recordFetchedBlock = populateUserArray
queryOperation.queryCompletionBlock = { cursor, error in
if cursor != nil {
print("There is more data to fetch")
self.fetchRecords(cursor!)
}
}
database.addOperation(queryOperation)
}
func fetchRecords(cursor: CKQueryCursor?) {
let database = CKContainer.defaultContainer().privateCloudDatabase
let queryOperation = CKQueryOperation(cursor: cursor!)
queryOperation.qualityOfService = .UserInitiated
queryOperation.recordFetchedBlock = populateUserArray
queryOperation.queryCompletionBlock = { cursor, error in
if cursor != nil {
print("More data to fetch")
self.fetchRecords(cursor!)
} else {
print(userArray)
}
}
database.addOperation(queryOperation)
}
func populateUserArray(record: CKRecord) {
let name = record.objectForKey("Name") as! String!
let company = record.objectForKey("Company") as! String!
let dateInductionCompleted = record.objectForKey("DateInductionCompleted") as! NSDate!
var image = UIImage()
let imageAsset = record.objectForKey("Image") as! CKAsset!
if let url = imageAsset.fileURL as NSURL? {
let imageData = NSData(contentsOfURL:url)
let mainQueue = NSOperationQueue.mainQueue()
mainQueue.addOperationWithBlock() {
image = UIImage(data: imageData!)!
userArray.append(User(name: name, company: company, image: image, dateInductionCompleted: dateInductionCompleted))
}
}
print(userArray.count)
}
Could you try setting:
queryOperation.qualityOfService = .UserInitiated
This will indicate that your user interaction requires the data.
Otherwise it could happen that de request is ignored completely.
As discussed below the actual answer was that you should not re-use completion blocks. Instead you should create a recursive function for fetching the next records from a cursor. A sample of that can be found at: EVCloudKitDao
İ have a Parse app. İ want to get data from Parse class or database but for current user.
This is a poorly asked question, you provide no code nor do you show any effort on your own part to solve the problem. However, I myself remember struggling with this quite a but so here is how I accomplish this...
var arrayForData = Array()
var arrayForMoreData = Array()
var arrayForEvenMoreData = Array()
func fetchUserData() {
let userQuery: PFQuery = PFUser.query()!
userQuery.whereKey("username", equalTo: (currentUser?.username)!)
userQuery.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
var someData = users!
var someMoreData = users!
var thirdSetOfData = users!
if error == nil {
if someData.count >= 1 {
for i in 0...users!.count-1 {
self.arrayForData.append(someData[i].valueForKey("columnNameParse1") as! Int)
self.arrayForMoreData.append(someMoreData[i].valueForKey("columnNameParse2") as! Double)
self.arrayForEvenMoreData.append(thirdSetOfData[i].valueForKey("columnNameParse3") as! String)
}
}
self.usersArray = users as! [PFUser]
self.tableView.reloadData()
} else {
print(error)
}
})
}