Swift: Parse database doesn't update object - swift

I'm building an app and I need a connection with a database. I'm using Parse for this (and hosting it with back4app if that does matter).
I have already a lot of experience with Parse so it's very weird that I have this problem.
So I need to update an object but it doesn't. Could somebody please help me out? I've putted in my code an print and it does print so you know the app get's there but strangely enough, it doesn't update the object.
Thanks in advance!
EDIT: the code works because when I run the app in the simulator it updates but when I run it on my iPhone, it doens't?
func makeMessagesSeen() {
let query = PFQuery(className: "Chat")
query.whereKey("senderID", equalTo: otherID)
query.whereKey("receiverID", equalTo: yourID)
query.order(byDescending: "createdAt")
var objects = try! query.findObjects()
for i in objects {
if (objects[0].objectId == i.objectId) {
print("this will print , idk why code beneath this doesn't work?")
i["messageSeen"] = "last seen"
i.saveInBackground()
}
else {
i["messageSeen"] = "seen"
i.saveInBackground()
}
}
}

I fixed it and will post the solution here in case anyone will make the same mistake.
So I created the message object with my simulator and parse automatically set the write acces for everyone to false. Only for the device that created the message it was set to true. So that's why I could only update to object with my simulator.
So change in your database the write acces for everyone to true.

Related

Why does the display name of my users keep reverting in Firebase with swift?

I encountered the following error in my swift project: https://github.com/firebase/firebase-ios-sdk/issues/4393. To overcome this issue I change the display name to the characters before the # in a users email like so:
var displayName = user.email!
if let atRange = displayName.range(of: "#") {
displayName.removeSubrange(atRange.lowerBound.. < displayName.endIndex)
}
if user.displayName!.count < 2{
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = displayName
changeRequest?.commitChanges {(err) in
if let err = err{
print(err)
}
}
}
This works when I first log in but if I log out and then back in again the display name reverts back to Optional(""). Why would it be doing this?
Edit
I created a workaround where I run the above code not only when a user is first created, but every time a user logs in, this seems excessive though and there must be a reason why the display name keeps getting overwritten.
This is not an error, this is intended behaviour from Apple's side. By default, you should be updating the Firebase user with the appropriate display name rather than relying on 3rd party information since these are only available on first login.
Simply checking if the name exists on initial login and is longer than 0 characters, not null, etc. then you can update the name with the following:
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = displayName
changeRequest?.commitChanges { (error) in
// ...
}
This is also documented within the git-issue you posted https://github.com/firebase/firebase-ios-sdk/issues/4393#issuecomment-612193703
Ultimately, you are logging into your app as a Firebase user, not an apple user. Apple is only Authenticating the process, so you must update the Firebase user where possible as Apple is highly inconsistent across all platforms.

Why doesn't Deletion in Parse-server require to obtain that object beforehand?

I came across a strange behaviour. Even though my unit tests are passing, I don;t quite understand this.
func testDeleteMoment() {
// Create a Moment for current user
let expectCreate = expectation(description: "create moment should succeed")
Datastore.shared.createMoment(notes: "myNotes", rating: 1, time: Date(), location: "", isPublic: true, completion: {(success, error) in
XCTAssertTrue(success)
expectCreate.fulfill()
})
waitForExpectations(timeout: 5) { (error) in
XCTAssertNil(error, "Test timed out. \(String(describing: error?.localizedDescription))")
}
let query = PFQuery(className: "Moment")
var objectId = ""
do {
query.whereKey("owner", equalTo:PFUser.current()!)
let object = try query.getFirstObject()
objectId = object.objectId!
} catch {}
let task = Datastore.shared.deleteMoment(id: objectId)
task.waitUntilFinished()
let query2 = PFQuery(className: "Moment")
query2.whereKey("owner", equalTo:PFUser.current()!)
let task3 = query2.countObjectsInBackground()
task3.waitUntilFinished()
XCTAssertEqual(task3.result, 0)
}
While writing my datastore.deleteMoment(), I noticed that unlike saveEventually(), deleteEventually() doesn't have a completion handler. Instead, it only comes with a BFTask<NSNumber>. Hence I experimented with the following code and the unit test passes to my surprise.
func deleteMoment(id: String) -> BFTask<NSNumber> {
let pfMoment = PFObject(className:"Moment")
pfMoment.objectId = id
return pfMoment.deleteEventually()
}
How comes that I don't have to retrieve the object before hand in order to delete it? Why isn't there then just a method to delete the object via an id, instead of doing it like this?
How comes that I don't have to retrieve the object before hand in order to delete it?
Parse lets you work with "shell objects". Basically, if you know the object id you can attach it to a blank instance of a Parse Object subclass and do some actions on it. You can create a shell, assign a couple values, and call object.save() and it will update just those fields. You can create a blank object, assign an id, then call object.fetch() to obtain it, without having to have gotten the object from a query or pointer on another object.
The reason it works is because in order to perform the database operations, the object id and class name are the only pieces of information required. If you just want to update a couple fields, you don't need to pull all of the rest of the data to do so. And if you want to destroy an object, why would you need all of it's data locally first? You just need to remove the entry in the database with the matching _id.
Why isn't there then just a method to delete the object via an id, instead of doing it like this?
Nobody has built it yet. Parse is open source, and while it's a pretty phenomenal resource today, there's certainly a lot of room for improvement. Feel free to add this feature and create a PR. Though you could easily build your own wrapper by extending the Parse.Object class with this function, that basically does what you already did under the hood.

Swift + Locksmith: Not storing value

I am trying to store a value at the end of a game.
I am using the https://github.com/matthewpalmer/Locksmith updateData method.
It says on the Github Repo
as well as replacing existing data, this writes data to the keychain if it does not exist already
try Locksmith.updateData(["some key": "another value"], forUserAccount: "myUserAccount")
This is what I have:
let dictionary = Locksmith.loadDataForUserAccount("gameKeyChainValues")
print("Ended \(EndDateAndTimeString)")
do {
try Locksmith.updateData(["BattleEnd": "12345678"], forUserAccount: "gameKeyChainValues")
}
catch {
print("Unable to set time")
}
print("This line ran")
if let timeBattleEnded = dictionary!["BattleEnd"] as? String {
print("Stored for END: \(timeBattleEnded)")
}
This line print("Ended (EndDateAndTimeString)") outputs:
Ended 19:33:38+2016-08-05
This line print("Unable to set time") does nothing
This line print("This line ran") outputs:
This line ran
This line: print("Stored for END: (timeBattleEnded)") does nothing.
When I set a break point and then type po dictionary in the console it shows me other things that are set but not this value.
Can anyone see why?
EDIT:
So, After checking the console. It does appear that directly after I save the information to the Keychain it is there. Then I switch views and update another item in the keychain. This then seems to delete the original one and only keep the new item. Both have different names.
Any ideas?
In the exact words of matthew palmer:
Only one piece of data is stored under an account, so when you call updateData it will overwrite whatever's currently stored for that account
so, Everytime you call Locksmith.updateData it basically clears all the data in there and then adds the new value. You need to send both keys and values together.
Try this:
try Locksmith.updateData(["BattleEnd": "12345678", "Key2": "Value Two"], forUserAccount: "gameKeyChainValues")
The dictionary you fetch in the first line isn't a constantly updating view of the keychain, it's just a copy of what's in the keychain when you call that method. It won't be updated when you update the keychain. If you want the up to date values out of the keychain after you update it, call loadDataForUserAccount() again after you update it.

How can you get information such as, users window for example using the NSTask class in Swift, for OSX apps?

If I can explain my self better knowing if a user is running some particular program such as word or chrome, etc.
I will try to update my question as I write code or find more information.
Thank you (:
You don't have to go through NSTask, you can use Cocoa's NSWorkspace to get the list of running applications:
import Cocoa
let apps = NSWorkspace.sharedWorkspace().runningApplications
It returns an array of NSRunningApplication objects.
Let's say you want the running applications names in an array:
let appsNames = apps.flatMap { $0.localizedName }
If you want to know for example if iTunes is running:
if appsNames.contains("iTunes") {
// iTunes is running
}
Note, a more precise way to find a running app would be to use the bundle identifier:
let bundleNames = apps.flatMap { $0.bundleIdentifier }
if bundleNames.contains("com.apple.iTunes") {
// iTunes is running
}
If you want to know which app is active (has its window frontmost):
let actives = apps.filter { $0.active }
if let active = actives.first {
// "active" is the frontmost app
}

Swift & Parse.com find by specific column not ID

Reference: http://blog.parse.com/2014/06/06/building-apps-with-parse-and-swift/
I'm trying to find a columns value: userPassword, based in the userName column. Using the above reference from Parse it shows that to get data from parse you should use:
var query = PFQuery(className: "GameScore")
query.getObjectInBackgroundWithId(gameScore.objectId) {
(scoreAgain: PFObject!, error: NSError!) -> Void in
if !error {
NSLog("%#", scoreAgain.objectForKey("playerName") as NSString)
} else {
NSLog("%#", error)
}
}
However, as you can see it is looking for (gameScore.objectId) - The problem is I do not know this value as the user isnt entering a complex parse generated ID. They're entering their chosen username. In the rows I have userName and Password set. How do I search the rows for the userPassword so I can verify it based on their specified userName.
Thanks in advance
Why are you querying the database for a username and password. Adding a new user is very simple with Parse. Taken directly from their docs:
Query User table on Parse
You can query the user table first, using a PFQuery:
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:username];
Adding New User
The idea of user accounts that let people access their information and share it with others in a secure manner is at the core of any social app. Whether your app creates its own sharing environment or integrates with existing social networks, you will need to add functionality to let people manage their accounts in your app.
We provide a specialized user class called PFUser that automatically handles much of the functionality required for user account management.
First make sure to include our SDK libraries from your .h file:
#import <Parse/Parse.h>
Then add this code into your app, for example in the viewDidLoad method (or inside another method that gets called when you run your app):
func myMethod() {
var user = PFUser()
user.username = "myUsername"
user.password = "myPassword"
user.email = "email#example.com"
// other fields can be set just like with PFObject
user["phone"] = "415-392-0202"
user.signUpInBackgroundWithBlock {
(succeeded: Bool!, error: NSError!) -> Void in
if error == nil {
// Hooray! Let them use the app now.
} else {
let errorString = error.userInfo["error"] as NSString
// Show the errorString somewhere and let the user try again.
}
}
}
This call will asynchronously create a new user in your Parse app. Before it does this, it checks to make sure that both the username and email are unique. It also securely hashes the password in the cloud.
You can learn more about Users, including how to verify emails and handle read and write permissions to data, by visiting our docs.
Run your app. A new object of the class User will be sent to the Parse Cloud and saved. When you're ready, click the button below to test if a User was created.
Further
I created a tutorial about connecting to parse if you still wish to go down the route of querying the server manually:
http://ios-blog.co.uk/tutorials/swift-create-user-sign-up-based-app-with-parse-com-using-pfuser/