I am new to programming and am trying to develop an iOS app using Swift. However, I ran into a problem trying to retrieve previously saved information from Parse to use in my app.
To save the information I use the following code:
var user = PFUser.currentUser()
var number = self.phoneNumber.text as String
var insta = self.instagramUsername.text as String
var snapp = self.snapchatName.text as String
var twitter = self.twitterHandle.text as String
var bio = self.profileBio.text as String
var socialData = PFObject(className: "socialData")
socialData["phoneNumber"] = number
socialData["instagram"] = insta
socialData["snapchat"] = snapp
socialData["twitter"] = twitter
socialData["bio"] = bio
socialData["user"] = user
socialData.save()
This saves the information on Parse correctly but then when I try to retrieve the Instagram name or anything else specifically using a query I get an error. Here is how I have been trying to retrieve it:
var user = PFUser.currentUser()
var query = PFQuery(className:"socialData")
query.whereKey("user", equalTo: user)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
// The find succeeded.
// Do something with the found objects
var socialData = PFObject(className: "socialData")
let instaUsername = socialData["instagram"] as String
println(instaUsername)
} else {
// Log details of the failure
NSLog("Error: %# %#", error, error.userInfo!)
}
}
The logs say fatal error: unexpectedly found nil while unwrapping an Optional value. After the app crashes. Yet, the information is correctly displayed on Parse. Any help is greatly appreciated, thanks!
Remove this line within your findObjectsInBackgroundWithBlock block entirely:
var socialData = PFObject(className: "socialData")
and replace this line:
let instaUsername = socialData["instagram"] as String
with this:
let instaUsername = objects[0]["instagram"] as String
so you're actually utilizing the object array retrieved from the query, accessing the first object which matches the current user, then getting the "instagram" key's stored value from that PFObject dictionary.
Related
I have a list of users stored on Parse (backend), with a column called "profile_picture" that stores the user's profile picture. However, there are certain users without a profile picture, thus their profile_picture column is "undefined" (has no value).
I am using a searchbar to query through all the users and update the tableview with the user's profile pic and username. I do this by appending the username to var searchResults = String , and the profile pic to var searchImages = PFFile after the query like so:
let query = PFQuery.orQuery(withSubqueries: [usernameQuery!,fbUsername!, firstnameQuery!, lastnameQuery!]);
searchActive = true;
query.findObjectsInBackground { (objects, error) in
if error != nil {
print("There was an error getting userlist");
}
else {
if let users = objects {
self.searchResults.removeAll(keepingCapacity: false);
for object in users {
if let user = object.object(forKey: "username") as? String {
self.searchResults.append(user);
}
if let picture = object.object(forKey: "profile_picture") as? PFFile {
self.searchImages.append(picture);
}
self.searchTableView.reloadData();
}
The problem is that when the "profile_picture" column is empty, it does not append anything, which then means that the searchResults array (containing usernames) and the searchImages array (containing the PFFiles) have different sizes, which results in uncoordinated assignments of values. They are supposed to parallel arrays. And I'm using values in these arrays to assign values in a table cell.
Hope you guys understand my problem! Thanks in advance
So your username field is definitely not empty. I think you can add an else after you check whether profile_picture is nil or not. Like:
if let picture = object.object(forKey: "profile_picture") as? PFFile {
self.searchImages.append(picture);
} else {
self.searchImages.append(UIImage(named:"placeholder"))
}
might sound like a basic question--but I'm not seeing where I am going wrong..
I end up with either of these two scenarios:
I keep getting the error "Could not cast value of type __NSCFNumber to NSSTring". if I use extractedSku = skuList[i]!.value["sku"] as! String
If I remove as! String it saves it, but it isn't saved as a string. How do I get this to be saved as a string?
I have appended data from firebase into an array
skuArray = [AnyObject?]()
in viewDidLoad, I am iterating skuArray to extract the 'sku' and store into a variable.
var skuArray = [AnyObject?]()
var productDetailArray = [AnyObject?]()
data stored in Sku Array is:
[Optional(Snap (aRandomKey) {
active = 1;
sku = 888888;
})]
viewDidLoad:
let skuList = self.skuArray
for var i = 0; i < skuList.count ; ++i{
let extractedSku = skuList[i]!.value["sku"] as! String
// go into database and extract "products" details by sku
self.databaseRef.child("products/\(extractedSku)").observeEventType(.ChildAdded, withBlock: { (snapshot:FIRDataSnapshot) in
self.productDetailArray.append(snapshot)
})
Since the underlying type is NSNumber, use the stringValue property to get a String:
if let extractedSku = (skuList[i]?.value["sku"] as? NSNumber)?.stringValue {
// use extractedSku which is of type String
}
I'm having trouble getting my information back from a saved array in Parse. I'm saving the information like this.....
videoARY = [videoId, vidTitleText, vidDescription, vidIMG]
let videoSave = PFObject(className:"UserVideos")
videoSave["userObjectId"] = PFUser.currentUser()!.objectId
videoSave["vid\(saveValueLBL.text!)user"] = PFUser.currentUser()!.username
videoSave["vid\(saveValueLBL.text!)"] = videoARY
videoSave.saveInBackgroundWithBlock { (success, error ) -> Void in
if success == true
{
print("Success")
}
}
In a different viewcontroller I'm initializing an array like this....
var vid1array = [String]!()
and retrieving this way.......
let query = PFQuery(className: "UserVideos")
query.whereKey("userObjectId", equalTo: PFUser.currentUser()!.objectId!)
query.findObjectsInBackgroundWithBlock { (vid:Array?, error:NSError?) -> Void in
if !(error != nil)
{
for items in vid!
{
if let myfav1 = items["vid1"] as? NSArray
{
self.videoDescription1 = myfav1[2] as! String
self.videoTitle1 = myfav1[1] as! String
self.videoIMG1 = myfav1[3] as! String
print(self.videoDescription1)
print(self.videoIMG1)
print(self.videoTitle1)
self.vid1array.append(self.videoTitle1)
print(self.videoTitle1)
}
}
}
}
I'm printing a successful save and each element of the saved array. When I try to add one of those elements to vid1array the app crashes I get "Fatal error: unexpectedly found nil while unwrapping an Optional value". Please help.
* UPDATE *
When I print vid! I get this...
vid1 = (
"YiiqHq_kNnU",
"INFINITE \"Back\" Official MV",
"If you like this video, plz click \"LIKE\" and \"SUBSCRIBE\". INFINITE \"Back\" Official MV INFINITE Official Website : http://www.ifnt7.com INFINITE Official YouTube ...",
"https://i.ytimg.com/vi/YiiqHq_kNnU/default.jpg"
);
It's not nil but I don't see array brackets either, could that be it? I'm printing out the elements I just want to save them to my array so I can use them in my tableview later and the array is coming up nil.
* FOLLOW UP QUESTION *
Would it be advised to place this in viewWillAppear vs. I currently have it in viewDidLoad?
Is there a way to update a number on parse not just by replacing it or creating a new object id then deleting the old one but by actually incrementing the value from the stored value on parse and the new value being added to it. Ie.
On parse: Correct = 5 ; On app: Correct = 10 ; new value on parse: 15
This is my code but it just creates a new object id with the new values every time...
func saveScoresOnParse() {
scores["Right"] = rightAnswers
scores["Wrong"] = wrongAnswers
scores["Skipped"] = skippedQuestions
scores["User"] = PFUser.currentUser()
scores.saveInBackground()
}
On parse:
I want the values to be added up and as well as only have one objectid for each one.
let scoreQuery = PFQuery(className: "Scores")
scoreQuery.whereKey("user", equalTo: PFUser.currentUser()!)
scoreQuery.getFirstObjectInBackgroundWithBlock { (object, error) -> Void in
if error == nil {
if let scores = object {
scores["Right"] = rightAnswers
scores["Wrong"] = wrongAnswers
scores["Skipped"] = skippedQuestions
scores.saveInBackground()
}
}
}
Im having some difficulty with creating complex queries in Parse. What I'm looking to achieve is searching the _Users class and not returning myself as a result and if an Invite already exists in the Invites class, show Pending text rather than Add Button and if both myself and user have already accepted invite, dont show them at all.
I've achieved some of it but I'm not sure i'm doing it the most efficient way. For instance, I first query the _User class and find any users that match the search terms, then loop through and if the objectId == currentUser().objectId, skip that record. Then I run another query in that loop on each record to see if there are any pending invites, if so, i set a flag to true for Pending however i've done that search not in the background because i was having isses with the first query block finishing before the second and my flag not getting set first. So would I then do a third query to see if the field is accepted? Or is there a way to make this all one big query? My code is below for the searching:
func performSearchForText(text: String, completion: SearchComplete) {
state = .Loading
// Query Parse
var containsDisplayName = PFQuery(className:"_User")
containsDisplayName.whereKey("displayName", containsString: text)
var containsUsername = PFQuery(className: "_User")
containsUsername.whereKey("username", containsString: text)
var query = PFQuery.orQueryWithSubqueries([containsUsername, containsDisplayName])
query.findObjectsInBackgroundWithBlock {
(results: [AnyObject]?, error: NSError?) -> Void in
//self.state = .NotSearchedYet
var success = false
if error == nil {
// Found results
// Set Result state to either Results or NoResults
if let results = results {
//println(results)
if results.count == 0 {
self.state = .NoResults
} else {
// Read Results into UserSearchResult Array
// Dont show self in results
// If user has already accepted a request, dont show
// If they have already invited, show Pending instead of button
var userSearchResults = [UserSearchResult]()
var searchResult = UserSearchResult()
for result in results {
if result.objectId != PFUser.currentUser()?.objectId {
// Query invites table to see if they already are accepted with user
// or if a pending invite exists
// Set invite or accepted respectively
var invitedQuery = PFQuery(className: "Invites")
invitedQuery.whereKey("pending", equalTo: true)
invitedQuery.whereKey("inviteToUser", equalTo: result.objectId!!)
var invitedQueryResults = invitedQuery.findObjects()
if invitedQueryResults?.count > 0 {
self.pendingInvite = true
}
searchResult.displayName = result["displayName"] as! String
searchResult.emailAddress = result["username"] as! String
searchResult.inviteUserID = result.objectId!!
searchResult.invited = self.pendingInvite
searchResult.accepted = false
userSearchResults.append(searchResult)
}
}
if userSearchResults.count > 0 {
self.state = .Results(userSearchResults)
} else {
self.state = .NoResults
}
}
success = true
}
dispatch_async(dispatch_get_main_queue()) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
completion(success)
}
} else {
// Error, print it
println(error)
}
}
}
Unless there's a lot more to your invites table, I'd suggest removing it and adding an "invitePending" field to your user table.
Then you can just further refine your other queries by using wherekey("invitePending", equalTo: true).
Additionally, you can do something like this at the start so you don't have to check for currentUser in the query completion block:
containsDisplayName.whereKey("objectId", notEqualTo: PFUser.currentUser()!.objectId!)
But you definitely don't need the invites table if it isn't storing lots of other info. If you have to keep it as a separate table, using a pointer (for 1 to 1) or a PFRelation (for 1 to many) would save you the headache of that inner query.