Can't I use nil for 'undefined' on parse? - swift

I am making a simple app, and implementing the userInformation part. The user can edit his info, but I have trouble that if user doesn't put any info, it will crash when I try to retrieve data from an undefined column.
This is my code to retrieve the user data.now I can check a specific value, but still, I got a error with 'undefined' one.
var query = PFQuery(className: "Note")
query.getObjectInBackgroundWithId("kg8KhAWCms", block: {
(obj, error)in
if let obj = obj! as? PFObject {
let nickname = (obj.objectForKey("text")) as! String
if (nickname != nil) {
///// I have a error message that binary operator'!=' cannot be applied to operands of type 'String' and 'NiLiteralConvertible'
self.nickNameLabel.text = nickname
}else{
self.nickNameLabel.text = "you don't have a nick name"
}
} else {
print(error)
}
})
what is the 'NiLiteralConvertible' ?
and I've tried this as well,
var query = PFQuery(className: "Note")
query.getObjectInBackgroundWithId("kg8KhAWCms", block: {
(obj, error)in
if let obj = obj! as? PFObject {
let nickname = (obj.objectForKey("text")) as! String
if !(nickname.isEmpty) {
self.nickNameLabel.text = nickname
}else{
self.nickNameLabel.text = "you don't have a nick name"
}
} else {
print(error)
}
})
So I am asking how can I handle retrieving undefined value before crash? (please write full code for me)
///like this
if (undefined in parse == somekindOfType) {
print("yes")
}

You can't use nickname != nil because you have already said that it cannot be nil with let nickname = (obj.objectForKey("text")) as! String.
The as! String unwraps the obj.objectForKey("text") and at which it cannot be nil or you will get an error.
I suggest using the following:
if let nickname = obj.objectForKey("text") as? String {
self.nickNameLabel.text = nickname
}
else{
self.nickNameLabel.text = "you don't have a nick name"
}
Perhaps this would be beneficial to read: Swift Literal Convertibles

Related

Problems retrieving a record from CloudKit

Having problems retrieving a record from CloudKit..
My firstName and lastName of my CKRecord are showing up as "N/A" which the safety value from a nil coalescing as you'll see below
I double and triple checked (and beyond) that all the spellings were right.. so I'm good there. Here is my retrieve method..
func getProfile() {
//GETTING USER RECORD ID
CKContainer.default().fetchUserRecordID { id, error in
guard let id = id, error == nil else {
print(error?.localizedDescription)
return }
//GETTING RECORD ID
CKContainer.default().publicCloudDatabase.fetch(withRecordID: id) { record, error in
guard let record = record, error == nil else { return }
let profileReference = record["userProfile"] as! CKRecord.Reference
let profileRecordID = profileReference.recordID
print("Profile reference is",profileReference)
//PASSING REFERENCE TO GET CLIENT SIDE MODEL
CKContainer.default().publicCloudDatabase.fetch(withRecordID: profileRecordID) { profileRecord, error in
guard let profileRecord = profileRecord, error == nil else {
print(error!.localizedDescription)
return
}
//FOR SOME REASON MY PROFILE IS NOT RECEIVING THE RIGHT VALUES FOR FIRST AND LAST NAME, IT JUST SHOWS "N/A"
DispatchQueue.main.async {
let profile = Profile(record: profileRecord)
print("Retrieved Record is: ",profileRecord)
print("Retrieved name is: \(profile.firstName)")
firstName = profile.firstName
lastName = profile.lastName
}
}
}
}
}
And here is the model..
struct Profile {
let profileID: CKRecord.ID
let firstName: String
let lastName: String
init(record: CKRecord) {
profileID = record.recordID
firstName = record["firstName"] as? String ?? "N/A"
lastName = record["lastName"] as? String ?? "N/A"
}
}

Always getting nil in completion

I'm trying to get Map data I have in Firestore, this is how it looks:
I'm trying to get the data, and create an array of Friend Object and return the array in the completion handler.
This is what I have:
func fetchFriendList(_ id: String, completion: #escaping([Friend]?)->()) {
var fetchedFriends: [Friend]?
db.collection(USERS_COLLECTION).document(id).getDocument { (doc, err) in
if err == nil && doc != nil {
guard let results = doc?.data()?[USER_FOLLOWING] as? [String: Any] else { return }
for result in results { // Getting the data in firebase
if let resultValue = result.value as? [String: Any] { // Getting only the value of the MAP data, we do not need the key.
//Getting the fields from the result
guard let id = resultValue[FRIEND_ID] as? String else { return }
guard let profilePic = resultValue[FRIEND_PROFILE_PIC] as? String else { return }
guard let username = resultValue[FRIEND_NAME] as? String else { return }
guard let email = resultValue[FRIEND_MAIL] as? String else { return }
//Creating a new Friend object from the fields
let friend = Friend(id: id, profilePicture: profilePic, username: username, email: email)
fetchedFriends?.append(friend)
}
completion(fetchedFriends)
}
}else {
print(err!.localizedDescription)
completion(nil)
}
}
}
I tried printing the results, and resultValue etc, they are not nil.
But, after trying to append and print the fetchedFriends Array, I get nil, and the completion is also nil.
I don't really understand why this is happening.
The problem is that you haven't initialized variable fetchedFriends and you have used optional type when appending data to it. Since it has not been initialized, it will skip appending to it. You should initialize it in the beginning. The updated code would be as follows.
func fetchFriendList(_ id: String, completion: #escaping([Friend]?)->()) {
var fetchedFriends: [Friend] = []
db.collection(USERS_COLLECTION).document(id).getDocument { (doc, err) in
if err == nil && doc != nil {
guard let results = doc?.data()?[USER_FOLLOWING] as? [String: Any] else { return }
for result in results { // Getting the data in firebase
if let resultValue = result.value as? [String: Any] { // Getting only the value of the MAP data, we do not need the key.
//Getting the fields from the result
guard let id = resultValue[FRIEND_ID] as? String else { return }
guard let profilePic = resultValue[FRIEND_PROFILE_PIC] as? String else { return }
guard let username = resultValue[FRIEND_NAME] as? String else { return }
guard let email = resultValue[FRIEND_MAIL] as? String else { return }
//Creating a new Friend object from the fields
let friend = Friend(id: id, profilePicture: profilePic, username: username, email: email)
fetchedFriends.append(friend)
}
completion(fetchedFriends)
}
}else {
print(err!.localizedDescription)
completion(nil)
}
}
}
Hope it helps.

Else on If Else statement won't get triggered, can't understand why

I have this block of code:
func fetchFriends() {
if let window = UIApplication.shared.keyWindow {
guard let userId = Auth.auth().currentUser?.uid else { return }
DispatchQueue.main.async {
FirestoreService.shared.fetchFriendList(userId) { (fetchedFriends) in
//// WONT GET HERE ////
if fetchedFriends != nil {
self.fetchedFriends = fetchedFriends! // Can force unwrap here because we already know that fetchedFriends in not nil.
self.friendsTable.reloadData()
}else {
self.fetchedFriends = []
self.friendsTable.reloadData()
}
}
}
}
}
This block of code is using this function:
func fetchFriendList(_ id: String, completion: #escaping([Friend]?)->()) {
var fetchedFriends: [Friend] = []
db.collection(USERS_COLLECTION).document(id).getDocument { (doc, err) in
if err == nil && doc != nil {
guard let results = doc?.data()?[USER_FOLLOWING] as? [String: Any] else { return }
for result in results { // Getting the data in firebase
if let resultValue = result.value as? [String: Any] { // Getting only the value of the MAP data, we do not need the key.
//Getting the fields from the result
guard let id = resultValue[FRIEND_ID] as? String else { return }
guard let profilePic = resultValue[FRIEND_PROFILE_PIC] as? String else { return }
guard let username = resultValue[FRIEND_NAME] as? String else { return }
guard let email = resultValue[FRIEND_MAIL] as? String else { return }
//Creating a new Friend object from the fields
let friend = Friend(id: id, profilePicture: profilePic, username: username, email: email)
fetchedFriends.append(friend)
}
completion(fetchedFriends)
}
}else {
print(err!.localizedDescription)
completion(fetchedFriends)
}
}
}
Whats happening here, is that I'm going into a user's document, getting it's 'Friends' from a Map I have in the document, creating a Friend Array and sending it in the completion to the first function.
In the first function, I'm checking if what I got is nil, if not, I'm assigning it to an array, else, if it is nil, I want the array to be empty.
The purpose here is to show the "Friends" in the tableView if the user has any.
My problem is this situation:
For start, the list of friends is empty, adding a friend and viewing the list, the friend I just added is showing, which is good. the problem is, when I'm removing this friend from the list (and it is deleted in the Database in Firestore), showing the list again does not deletes him from the list and still showing it.
It seems that after removing a friend from the "following" section, and showing the list again, after FirestoreService.shared... it just returns and won't get to the "Won't get here" line.
The FetchFriends() function does gets called everytime I'm opening the FriendsList.
This is a picture of the list I'm referring to, this demouser is removed from the friends list but still showing up.
EDIT: Just noticed that when I have more than one user on the list, it does gets deleted and it works as I want. When I have just one user (or just one left on the list) it won't delete it.
fetchFriendList never calls the callback with a nil value:
var fetchedFriends: [Friend] = []
Therefore your else branch is unnecessary and the completion handler could be #escaping ([Friend]) -> Void without optionals.
By the way, there is also a situation when your method does not call completion at all:
guard let results = doc?.data()?[USER_FOLLOWING] as? [String: Any] else { return }
In general, there are many unsafe places. For example, when err is nil and doc is nil, then your else will crash unwraping err!.
A better alternative:
guard err == nil, let doc = doc else {
print(err?.localizedDescription)
completion([])
return
}
let results = (doc.data()?[USER_FOLLOWING] as? [String: Any]) ?? [:]
let fetchedFriends = results.compactMap { result in
guard
let resultValue = result.value as? [String: Any],
let id = resultValue[FRIEND_ID] as? String,
let profilePic = resultValue[FRIEND_PROFILE_PIC] as? String,
let username = resultValue[FRIEND_NAME] as? String,
let email = resultValue[FRIEND_MAIL] as? String
else { return nil }
return Friend(id: id, profilePicture: profilePic, username: username, email: email)
}
completion(fetchedFriends)

How can I resolve type of expression is ambiguous error?

In this block of code, the
" let remoteUser = try User(record: postDict, context: DatabaseManager.shared.persistentContainer.newBackgroundContext())
"
line generates a type of error is ambiguous without more context error.
I am a little unsure why this error is generated -- however, I do know that I have two user objects -- a firebase User class, and a User class local to my application. When I specify the User as my modulename.User, the same error is raised. When I remove this user logic, the application builds correctly.
Auth.auth().signIn(withEmail: emailAddress, password: password) { (user, error) in
// If Successful, pull firstName/lastName
let ref: DatabaseReference!
ref = Database.database().reference().child("users").child(emailAddress)
ref!.observe(DataEventType.value, with: { (snapshot) in
// Get user value
let postDict = snapshot.value as? NSDictionary ?? [:]
let firstName = postDict["firstName"] as? String ?? ""
let lastName = postDict["lastName"] as? String ?? ""
let showroom = postDict["showroom"] as! NSDictionary
let showroomReference = showroom["showroomID"] as? String ?? ""
if showroomReference == "" {
completionHandler(.failure(Error.unknownShowroom))
}
let remoteUser = try User(record: postDict, context: DatabaseManager.shared.persistentContainer.newBackgroundContext())
self.fetchShowroom(forIdentifier: showroomReference) { (result) in
do
{
let showroom = try result.value()
remoteUser.showroom = showroom
remoteUser.brands = showroom.brands
completionHandler(.success(remoteUser))
}
catch
{
completionHandler(.failure(error))
}
}
})
}
I'd appreciate any help - thanks! XCode does not build unless I resolve this error.
The error is a bit misleading. The do - catch block around the try statement is missing.
do {
let remoteUser = try User(record: postDict, context: DatabaseManager.shared.persistentContainer.newBackgroundContext()
self.fetchShowroom(forIdentifier: showroomReference) { (result) in
do {
let showroom = try result.value()
remoteUser.showroom = showroom
remoteUser.brands = showroom.brands
completionHandler(.success(remoteUser))
} catch {
completionHandler(.failure(error))
}
}
} catch {
completionHandler(.failure(error))
}

Getting username of Parse user

I have code that i am trying to get a username of a PFUser. I'm getting the user and able to print it out, but when I get to the line for the username, the code just stops. No crash nothing, just stops running?
Any ideas why it would do that?
print(employeeObject)
firstName.text = employeeObject.firstName
lastName.text = employeeObject.lastName
if employeeObject.roleType != nil {
roleLabel.text = employeeObject.roleType?.roleName
}
if employeeObject.objectForKey("userPointer") != nil {
employeeObject.userPoint = employeeObject.objectForKey("userPointer") as! PFUser
}
if employeeObject.userPoint != nil {
let userID = employeeObject.userPoint!.objectId!
let query = PFUser.query()
query?.whereKey("objectId", equalTo: userID)
query?.getFirstObjectInBackgroundWithBlock({ (userData : PFObject?, error : NSError?) in
print(userData)
self.userLoginSwitch.setOn(true, animated: false)
self.userNameTextField.text = self.employeeObject.userPoint?.username!
self.passwordTextField.text = ""
self.emailAddressTextField.text = self.employeeObject.userPoint!.email
if self.employeeObject.userPoint!.objectForKey("isAdmin") as! Bool {
self.adminSwitch.setOn(true, animated: false)
}
})
}
if employeeObject.active {
disableEnableEmployee.setTitle("Disable Employee", forState: .Normal)
} else {
disableEnableEmployee.setTitle("Enable Employee", forState: .Normal)
disableEnableEmployee.setTitleColor(UIColor.blueColor(), forState: .Normal)
}
I have userPoint casted as a PFUser, and then in Parse i have it pointed to the User table, and when i print. i can see all the information. It just stops working thought, with no error or explanation.
var userPoint : PFUser? {
get {return objectForKey("userPointer") as? PFUser}
set { setObject(newValue!, forKey: "userPointer") }
}
When you fetching objects from Parse pointer objects are not included by default. There is only PFObject with objectId. If you want to include all of properties of pointer you need to use includeKey method with name of pointer. So if you get employeeObject from query (somewhere earlier) you should add line:
employeeQuery.includeKey("userPointer")
In this solution you wont need to get this user again because its the same object.
#Mazels answer is the second solution. You getting the same user by objectId so you can read data from usedData
self.userNameTextField.text = userData["username"]
self.emailAddressTextField.text = userData["email"]
if userData["isAdmin"] as! Bool { ...
Last thing:
if employeeObject.objectForKey("userPointer") != nil {
employeeObject.userPoint = employeeObject.objectForKey("userPointer") as! PFUser
}
This is redundant. Look at your implementation of userPoint and this if statement. It really do nothing :)
if you want to print the username from the query it has to be from the object that is returned, in your case userData and are you sure you dont have any break point in your code if it just stops?
self.userNameTextField.text = userData.username