func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
// Set up the Parse SDK
let configuration = ParseClientConfiguration {
$0.applicationId = "WhatsTheHW"
$0.server = "https://whatsthehw-parse-alan.herokuapp.com/parse"
}
Parse.initializeWithConfiguration(configuration)
let query = PFQuery(className: "Course")
query.findObjectsInBackgroundWithBlock {(result: [PFObject]?, error: NSError?) -> Void in
for object in result! {
// existing objectIds: 1Ja2Hx77zA, 34AF1vKO6f, 5FWlsswxw0
if object.objectId == "34AF1vKO6f" {
object["studentRelation"] = ["hi", "ih"]
object.saveInBackgroundWithBlock{(success, error) in
if success == true {
print("\(object) saved to parse")
} else {
print("save failed: \(error)")
}
}
}
}
}
return true
}
This is the minimum I can reduce this task to (this code is at AppDelegate).
It all worked fine when I tried using REST api and api console in parse dashboard but it doesn't work with iOS sdk.
The error I'm getting from the print statement is
Error Domain=Parse Code=101 "Object not found." UserInfo={code=101, temporary=0, error=Object not found., NSLocalizedDescription=Object not found.}
It works if I'm simply adding a new object like this :
let object = PFObject(className: "Course")
object["name"] = "German"
object["studentRelation"] = ["a", "b"]
object.saveInBackgroundWithBlock{(success, error) in
if success == true {
print("save completed")
print("\(object) saved to parse")
} else {
print("save failed: \(error)")
}
}
I'm really lost and I don't know why this is happening.
Thanks in advance.
This issue might relate to the access rights (ACL) of the object that you're trying to save. [Error]: Object not found is printed when a user that doesn't have write access to an object tries to save it, the error message of the Parse SDK is really misleading here!
Make sure the parse user who is trying to save the object has the proper writes to actually write on that object in your parse DB.
An easy fix will be to set the default ACL inside your app to public read + write:
let acl = PFACL()
acl.publicReadAccess = true
acl.publicWriteAccess = true
PFACL.setDefaultACL(acl, withAccessForCurrentUser: true)
Be careful with this approach though, usually you want to set access rights according to the actual role of the user. So a better alternative would be to only set the ACL on the PFObject when you're creating it and only give write access to the users you know should be able to alter the object.
can you please first find your object, save into another object and run the saveInBackground outside of the loop.
Your code should look like the following:
var objectToSave : PFObject?
let query = PFQuery(className: "Course")
query.findObjectsInBackgroundWithBlock {(result: [PFObject]?, error: NSError?) -> Void in
for object in result! {
if object.objectId == "jMIxdSXNRH" {
objectToSave = object
}
}
if objectToSave != nil {
objectToSave!["studentRelation"] = ["hi", "ih"]
objectToSave!.saveInBackgroundWithBlock{(success, error) in
if success == true {
print("\(objectToSave) saved to parse")
} else {
print("save failed: \(error)")
}
}
}
}
I am using my objectId's and not your so please change them to yours :)
Related
I'm newish to Swift and new to Firestore and am running into an issue that I can't solve. I have the code below which is supposed to check for a document at the UserReference location in Firestore. If it doesn't exist, it should create a new document that contains the pertinent user information that I have previously grabbed from facebook.
This what UserReference looks like self.UserReference = self.db.collection("users").document(UserID as! String) where the UserID is from the Facebook graph request. Next, it'll run a transaction to pull the document, update the user doc with the latest facebook info (assuming this is not a new user), and then update the doc to Firebase Firestore.
let db = Firestore.firestore()
var fbUserUpdate = User(
firstName: String(),
lastName: String(),
email: String(),
<extra stuff>)
func updateFacebookInfoToFirestore(completion: #escaping (_: User?, _: Error?) -> Void) {
// Check if user exists, if not create new user
UserReference!.getDocument { (document, error) in
if document != nil {
// continue
} else {
self.UserReference.setData(self.fbUserUpdate.dictionary)
}
}
// Now pull data and update based on most recent facebook info.
db.runTransaction({ (transaction, errorPointer) -> Any? in
// Read data from Firestore inside the transaction, so we don't accidentally
// update using staled client data. Error if we're unable to read here.
let UserSnapshot: DocumentSnapshot
do {
try UserSnapshot = transaction.getDocument(self.UserReference!)
} catch let error as NSError {
errorPointer?.pointee = error
return nil
}
// This will overwrite the fbUserUpdate Struct with the updated information.
guard var dbUser = User(dictionary: UserSnapshot.data()) else {
let error = NSError(domain: "Domain", code: 0, userInfo: [
NSLocalizedDescriptionKey: "Unable to write to userdata at Firestore path: \(self.UserReference!.path)"
])
errorPointer?.pointee = error
return nil
}
// Update from Facebook
dbUser.firstName = self.fbUserUpdate.firstName
dbUser.lastName = self.fbUserUpdate.lastName
dbUser.email = self.fbUserUpdate.email
// Load new data to firestore
transaction.setData(dbUser.dictionary, forDocument: self.UserReference!, options: SetOptions.merge())
return nil
}) { (object, error) in
if let error = error {
print(error)
} else {
// nothing yet
}
}
}
However, when I run this in my app, when I get to the UserReference!.getDocument closure, it skips over the closure and then the transaction doesn't work as intended because the try UserSnapshot = transaction.getDocument(self.UserReference!) returns a null since no document exists.
I believe the issue is in the .getDocument closure, but I don't know where I'm going wrong. I've tried to emulate the FriendlyEats firestore example code as best I can but I'm stuck and am in need of an extra set of eyes.
I'm trying to check if username is already taken in parse or not, but seems don't work with my code, can you please what i'm doing wrong on it
Thanks
func usernameIsTaken(userName: String) -> Bool {
let userName = userNameTextField.text
let myUser: PFUser = PFUser.currentUser()!
//bool to see if username is taken
var isTaken : Bool = false
//access PFUsers
let query = PFUser.query()
query!.whereKey("username", equalTo: userName!)
query!.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]? , error : NSError? ) in
if error == nil {
if (objects!.count > 0) {
isTaken = true
print("username is taken")
} else {
print("Username is available. ")
}
} else {
print("error")
}
}
return isTaken
}
For one, you can attempt to sign the user up and Parse will return an error code of 202 if the username has already been taken.
http://parse.com/docs/dotnet/api/html/T_Parse_ParseException_ErrorCode.htm
If this isn't your intended use, to query the User table, use PFUser.query to construct a query instead.
Try this :
query.findObjectsInBackgroundWithBlock({ (object: [PFObject]?,error: NSError?) -> Void in
if error == nil {
}
})
With that being said if your only Interested in the count parse introduced a new method similar to findObjectsInBackground but does exactly what you are looking for, the method is called countObjectsInBackground
You can call this method after you define your query.
like so
query.countObjectsInBackgroundWithBlock { (count: Int32,error: NSError?) -> Void in
if error == nil {
//code here
}
Hope this helps
I am trying to make changes to the data already stored in the core data in Parse. But it is not making the necessary changes. And I looked at the documentation for parse regarding objects here: https://parse.com/docs/ios/guide#objects. And it seems that I am doing exactly what the document is telling me to do. Maybe I am missing something? Here is my code:
import UIKit
import Parse
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var products = PFObject(className:"Products")
products["name"] = "ice cream"
products["description"] = "Strawberry"
products["price"] = 4.99
products.saveInBackgroundWithBlock {
(success, error) -> Void in
if (success) {
println("Object saved with id \(products.objectId)")
} else {
println("Not successful!")
print(error)
}
}
//ignore the code below this line for now please :)
var query = PFQuery(className: "Products")
query.getObjectInBackgroundWithId("7lmnxHxibK", block: { (object: PFObject?, error) -> Void in
if error != nil {
print(error)
} else if let product = object {
print("YO")
product["description"] = "Rocky Road"
product["price"] = 5.99
products.saveInBackground()
}
})
}
}
So the code above created an object with the ID 7lmnxHxibK. The description being Strawberry, the name being ice cream, and the price being 4.99. Which worked as it should. So now as an attempt to change the attributes in the object with the ID 7lmnxHxibK, I wrote the following code:
var query = PFQuery(className: "Products")
query.getObjectInBackgroundWithId("7lmnxHxibK", block: { (object: PFObject?, error) -> Void in
if error != nil {
print(error)
} else if let product = object {
print("YO")
product["description"] = "Rocky Road"
product["price"] = 5.99
products.saveInBackground()
}
})
This code should make the necessary changes to the object with the id 7lmnxHxibK. But rather than making the necessary changes to the object's attributes, it is creating a new object with it's description, name, and price all being (undefined). Anybody have a solution to fix this?
You have to change products.saveInBackground() to product.saveInBackground(). Also by the time you call your second query parse may not be done saving the object for the first time.
I am having an issue using Xcode 6.2 and Swift where I have a tuple that I am returning from a function. I have a subfunction that is running inside the function that is authenticating a user into our datastore and then returning the authentication tokens. If the user does not authenticate, i.e., has an error, then I am returning that correctly to the calling function. If the user passes authentication then I am only passing nil back to the calling controller even though I am making the same variable assignments. Here is the code that we are using:
func login(email:String, password:String) -> (uid: String?, provider: String?, error: NSError?) {
var errorStatement: NSError?
var provider: String?
var testResult: String?
var authData: FAuthData
ref.authUser(email, password: password) {
error, authData in
if error != nil {
// an error occured while attempting login
println("error is: \(error)")
errorStatement = error
testResult = "failed"
provider = "Error"
} else {
// user is logged in, check authData for data
testResult = "passed"
provider = authData.provider
userUID = authData.uid
println("User uid = \(userUID) and provider = \(provider)")
}
}
return (testResult, provider, errorStatement)
}
I am getting perfectly what I would expect when it has an error, but nothing when it is fine. I know this is going to be some simple fix but I cannot seem to find it. Thanks for any help as I am new to this language.
Here is the corrected code after I implemented the completion as suggested by #rdelmar
func login(email:String?, password:String?, completion: (result: String?, errorDesc: String?) -> Void) {
var errorStatement: String?
var testResult: String?
ref.authUser(email, password: password) {
error, authData in
if error != nil {
// an error occured while attempting login
println("error is: \(error)")
println(error.domain)
errorStatement = "errpor"//error.localizedDescription
testResult = "failed"
completion(result: testResult, errorDesc: errorStatement) //send the calling function the attached information
} else {
// user is l ogged in, check authData for data
testResult = "passed"
userUID = authData.uid
completion(result: testResult, errorDesc: errorStatement)//send the calling function the attached information
}
}
}
How can I wait until data is retrieved from parse.com?
This is the function I have that returns an empty string since the response from parse.com is too slow. If I put a breakpoint inside the success area it will break "long" after the data is needed. I guess there is a way to get the data synchronous so it will wait?
func getObjectId(localPersonId:NSString) -> NSString{
var currentObjectId:NSString = ""
var query = PFQuery(className:"myClass")
query.whereKey("personId", equalTo:localPersonId)
query.whereKey("groupId", equalTo:self.currentGroupId)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
// should not use a for loop since this should
// only return one row
for object in objects {
currentObjectId = object["objectId"] as NSString
}
} else {
// Log details of the failure
NSLog("Error: %# %#", error, error.userInfo!)
}
}
return currentObjectId
}
In this case the getObjectId function will return an empty string. Anyone?
I realize this is 3 months old but although the Parse docs are incredibly good/useful, there isn't a whole lot out there answering IOS Parse related questions.
This should work. It uses a completion handler, which is a simple way of dealing with this issue.
(for more on completion handlers in asynch context: https://thatthinginswift.com/completion-handlers/ )
func getObjectId(localPersonId:NSString, completionHandler: (currentObjectId: [String]) -> ()){
var currentObjectId:NSString = ""
var query = PFQuery(className:"myClass")
query.whereKey("personId", equalTo:localPersonId)
//query.whereKey("groupId", equalTo:self.currentGroupId)
query.findObjectsInBackgroundWithBlock {
(objects, error) -> Void in
if error == nil {
// should not use a for loop since this should
// only return one row
for object in objects {
completionHandler(currentObjectId: currentObjectId)
}
} else {
// Log details of the failure
NSLog("Error: %# %#", error!, error!.userInfo!)
}
}
}