Refreshing UIViewController Swift - ios

Ok in my app i have a uiviewcontroller for the current user profile and a tableview. When the user clicks a table view cell that contains the current users followers, i refresh the view controller with the new user profile. When i add my code in the viewDidLoad everytime i hit back in the navigation bar it goes back to the last user but when i keep clicking a table view cell the app gets a memory warning and crashes. Now i tried in the viewDidAppear and doing this didnt get no crashes or memory warnings but when i hit back it goes back to the current user
Here is how i set up my code in
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
userTableView.delegate = self
userTableView.dataSource = self
let query = PFUser.query()
query.whereKey("username", equalTo: ChartsViewController.otherUser.user)
query.getFirstObjectInBackgroundWithBlock {
(object: PFObject?, error: NSError?) -> Void in
if error != nil || object == nil {
println("The getFirstObject request failed.")
} else {
//The find succeeded.
self.userProfile = object as PFUser!
}
}
And my code when the table view cell is clicked
ChartsViewController.otherUser.user = object["username"] as String
self.navigationController!.pushViewController(self.storyboard!.instantiateViewControllerWithIdentifier("OtherUserProfileViewController") as UIViewController, animated: true)

Related

Table View not reloading in swift

i have a add favorite button feature in my app. when a user adds a shop to his favorites, it goes to his profile.
now the table view works but when a i click the favorite button and navigate to the profile viewcontroller i dont see the new data which is the shop i favorited. but when i close and open the app, the shop becomes visible.
i want it to be once a user favorites something it will be shown immediately in his profile
i used tableView.reloadData() but it is not working. what am i doing wrong?
here is my code:
func loadData(){// i call thiss function later in the viewDidLoad()
let userID = Auth.auth().currentUser!.uid
db.collection("Favorites").whereField("usersID", isEqualTo: userID).getDocuments(){
querySnapshot, error in
if let error = error {
print(error.localizedDescription)
}else
{
if(querySnapshot!.isEmpty){
//here put you didn't favorite any shops image/text
}else{
self.shops = querySnapshot!.documents.compactMap({addShop(dictionary: $0.data())})
DispatchQueue.main.async {
self.faveList.reloadData()
print("reloaded")
}
}
}
}
}
viewDidLoad() is called only once when UIViewController created. You can try calling it on viewWillAppear() method

SWIFT / iOS: Data takes a few seconds to load when screen appears

I've created a user profile screen, and when loaded, the data takes a few seconds to appear. I'm looking for a more elegant solution.
Here's the view controller in the storyboard:
As you can see, there is placeholder text. The issue is that this text appears very briefly when this screen is loaded (before the data is retrieved from the database).
Here is what the screen looks like when the data is fully loaded:
I've seen that an Activity Indicator on the previous screen may be a good solution. Can anyone verify this and point me to a good SWIFT tutorial or Stack Overflow solution?
UPDATE:
I'm loading the data on the previous View Controller as suggested. The issue that I'm running into is that the constants which I store my data are not accessible in prepareForSegue -
override func performSegueWithIdentifier(identifier: String, sender: AnyObject?) {
let query = PFQuery(className:"UserProfileData")
query.whereKey("username", equalTo: (PFUser.currentUser()?.username)!)
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
for object in objects {
let yourselfObject = object["yourself"] as! String?
let brideGroomObject = object["brideGroom"] as! String?
}
}
} else {
print(error)
}
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "RSVPToUserProfile" {
if let destination = segue.destinationViewController as? UserProfileViewController {
destination.yourselfPassed = yourselfObject
destination.brideGroomPassed = brideGroomObject
}
}
}
This one is actually very simple... Just remove the placeholder text from the storyboard. That way it won't "appear very briefly."
If you don't like the idea of a blank screen, then move the loading code to the place where the view is being presented. Don't present the view until after the loading code is complete. You will have to pass the data to the view controller at presentation time if you do this.
-- EDIT --
Do not override performSegueWithIdentifier:sender:! Doing so will not accomplish your goal.
I say again. Move the loading code to the place where you are calling perform segue, and delay the call until after your data is loaded. (I.E. move the perform segue call into the findObjectsInBackgroundWithBlock block after you get the items.

popViewController does not go back

I am making an app that is retrieving images from parse and showing it to a collection view. When the user taps on the images the image show in detail. I have a textfield that the user can change. When the user changes something they hit the save button and goes back to the collection view. The code that goes back to the collection view controller does not work.
#IBAction func saveButton(sender: AnyObject) {
// Use the sent country object or create a new country PFObject
if let saveInBackWithBlock = currentObject as PFObject? {
updateObject = currentObject! as PFObject
} else {
updateObject = PFObject(className:"Tops")
}
// Update the object
if let updateObject = updateObject {
updateObject["imageText"] = topsLabel.text
// Create a string of text that is used by search capabilites
var searchText = (topsLabel.text)
updateObject["searchText"] = searchText
// Update the record ACL such that the new record is only visible to the current user
updateObject.ACL = PFACL(user: PFUser.currentUser()!)
// Save the data back to the server in a background task
updateObject.saveInBackgroundWithBlock{
(success: Bool, error: NSError?) -> Void in
if (success) {
// The object has been saved.
} else {
// There was a problem, check error.description
}
}
print("saved")
}
self.navigationController?.popViewControllerAnimated(true)
}
I don't know why it doesn't go back to the previous view controller.Thank you
Is your parent view embedded in a navigation controller? also - how has the view been presented?
From the post you just added - you cannot pop a modal VC - try dismissing the view controller instead.
Check if it's embed in Navigation Controller, try:
Click StroryBoard
Select first viewController
Click Editor -> Embed in -> Navigation Controller
Hope it helps.

Parse PFUser.currentUser returns nil - Swift

I am logging in my users using Parse. As my app opens my LaunchVieWController determines whether users are already signed in or not:
override func viewDidLoad() {
super.viewDidLoad()
//Make sure cached users don't have to log in every time they open the app
var currentUser = PFUser.currentUser()
println(currentUser)
if currentUser != nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("alreadySignedIn", sender: self)
}
} else {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("showSignUpIn", sender: self)
}
}
}
If users are already signed in they are taken to the table view described below. If they are not logged in, they are taken to a view in which they can go to the signup view or the login view.
My signup works perfectly fine, and signs up users and then redirects them to the table view.
Here is the signup function (it is in a separate controller from the login function):
func processSignUp() {
var userEmailAddress = emailAddress.text
var userPassword = password.text
// Ensure username is lowercase
userEmailAddress = userEmailAddress.lowercaseString
// Add email address validation
// Start activity indicator
activityIndicator.hidden = false
activityIndicator.startAnimating()
// Create the user
var user = PFUser()
user.username = userEmailAddress
user.password = userPassword
user.email = userEmailAddress
user.signUpInBackgroundWithBlock {
(succeeded: Bool, error: NSError?) -> Void in
if error == nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("signInToNavigation", sender: self)
}
} else {
self.activityIndicator.stopAnimating()
if let message: AnyObject = error!.userInfo!["error"] {
self.message.text = "\(message)"
}
}
}
}
My login function looks like so:
#IBAction func signIn(sender: AnyObject) {
var userEmailAddress = emailAddress.text
userEmailAddress = userEmailAddress.lowercaseString
var userPassword = password.text
PFUser.logInWithUsernameInBackground(userEmailAddress, password:userPassword) {
(user: PFUser?, error: NSError?) -> Void in
if user != nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("signInToNavigation", sender: self)
}
} else {
if let message: AnyObject = error!.userInfo!["error"] {
self.message.text = "\(message)"
}
}
}
}
After users log in they are sent to a table view. In that table view I am trying to access the current user object in the following function, as I want to load different data based on who the user is:
override func queryForTable() -> PFQuery {
var query = PFQuery(className:"MyClass")
query.whereKey("userID", equalTo: PFUser.currentUser()!)
return query
}
When I try to log in with a user I get thrown the following error: "fatal error: unexpectedly found nil while unwrapping an Optional value". It seems like the PFUser.currentUser() object is nil.
When users are sent to the table view after signing up the PFUser.currentUser() object is set and works perfectly.
My guess is that this is because of the fact that the PFUser.logInWithUsernameInBackground is happening in the background and that my query is trying to get the PFUser.currentUser() object before it has been loaded. Is there anyway I can work around this issue? In the table view the value of PFUser.currentUser() is needed to load the table data. Can I somehow make sure that PFUser.currentUser() gets assigned with the current user object before the function gets called (for example, by not loading in users in the background thread)?
All help is much appreciated!
EDIT: I've updated this post with some more of my code to help highlight any bug that I may be missing.
I discovered that this problem appeared because the segue signInToNavigation was wired from the Login-button, instead from the login view controller itself. This resulted in the segue being executed before the login function was executed, and therefore the PFUser.currentUser()object was not yet assigned when the table view loaded. I solved this by rewiring the segues. Stupid slip-up on my side.
Thanks to Ryan Kreager and Wain for taking time to help me figure out this issue!
You can try checking if PFUser.currentUser() != nil instead to make sure it's being set right after login. If it's not being set right off the bat, you know there is a deeper login problem.
Also, try removing the dispatch_async(dispatch_get_main_queue()){ wrapper around your call to the segue.
It's unnecessary (logInWithUsernameInBackground already returns to the main thread) and I have a hunch that it's creating a racing condition where the local object is not being set first because Parse can't do any post-call cleanup since you're going right for the main thread.

Update UILabel and TextFields with Updated Data from Parse Database

I do hope all is well. I am new to Swift. I am building a shell for 2 social app ideas of mine. I have successfully completed login, signup, logout and minimal querying using Parse. Up until now, I have been progressing, however I have reached a mental block.
I created a User Profile ViewController that queries the current user's information from the database and initially displays the results on the in the controller; First Name, Last Name, and etc.
I also created an Edit Profile ViewController that enables the user to update their profile information and logout. So far I have been able to submit the changes to the database, however I am having a hard time having the UILabels and Text Fields update to display the new values.
Some additional insight: Becuase my main view that users are redirected to after successful signin/registration is embedded in a navigation controller which means the navigation bar is inferred.
Please help me!
This is the code for my UserProfileViewController. Please let me knwo if I need to clarify.
override func viewDidLoad() {
super.viewDidLoad()
//---------------------------------------
//User Profile View - this can go between viewDidLoad and viewDidAppear functions.
//---------------------------------------
let userImageFile = PFUser.currentUser()["userProfileImage"] as PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData!, error: NSError!) -> Void in
if error == nil {
var image = UIImage(data:imageData)
self.currentUserProfilePicture.image = image
}
}
var query = PFUser.query()
query.whereKey("objectId", equalTo: PFUser.currentUser().objectId)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
let user = PFUser.currentUser()
var firstname = PFUser.currentUser()["firstName"]! as String
var lastname = PFUser.currentUser()["lastName"]! as String
var aboutMe = PFUser.currentUser()["aboutMe"]! as String
self.firstName.text = firstName
self.lastName.text = lastname
self.aboutMe.text = aboutMe
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidAppear(animated: Bool) {
}
Hi and welcome to swift.
Are you using the prepareForSegue, for sending data between ViewControllers?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "yourIdentifier" //you set the identifier in the storyboard
{
var vc = segue.destinationViewController as! YourDestinationViewController //the class of the destination view controller
vc.someValue = value
vc.otherValue = otherValue
vc.dataArray = array //here you set the values in the destination view controller
}
}

Resources