How to pass data from parse table into table view in swift? - ios

I'm trying to develop my first app. This is kind of a restaurant and menu app. The first screen should be list of restaurants. This will list all the restaurants from Parse table. If one of the row(restaurant) is selected, it should segue to another view controller, but with chosen(restaurant's/personalized) menu. If any of the menu item is selected, then a third vc should open up with that menu details.
I've created story board design and segues, but not sure:
1) how to present restaurants from parse table into 1st VC ?
2) If selected, how to pass the resta.id from 1st vc to menuVc.
I've tried getting the table data from Parse to an array, but lost in how to pass it to didSelectRow in 1st VC and then to other VC respectively. Is there a way of taking the parse table data into rows for the restaurants table ?
var myRestaurant: [String] = [String]()
//Getting the data from the PFQuery class
var query = PFQuery(className: "Restaurants")
query.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
self.myRestaurant.append(object.objectForKey("ClassNameObject") as! String)
}
}
} else {
println("errorin fetching restaurant block")
}
}

Great to hear you're getting started with app development!
This is a very common scenario and will be a breeze to accomplish with the right tools. You can build out your own functionality for Parse within a TableViewController or you can use Parse's own PFQueryTableViewController to accomplish exactly what you want very easily.
Check out a tutorial to help get you started
Here's another just in case
The gist of it is, you must query Parse for data to fill the TableViewController's data source. With PFQueryTableViewController, all you have to do is specify a query in the function queryForTable(). PFQueryTableViewController is included in the ParseUI framework (along with a bunch of other great tools) which you will want to import.

Related

Pass array to another View Controller

I have Tab Bar Controller, where I have few view controllers, but I want to pass array of values (workoutNames) to another view in my Tab Bar Controller. I wonder what's best option to do this and I've decided to use way of passing data with property. But when I try to retrieve data I get empty array. I could also use firestore to retrieve data in my Second View Controller, but It lasts too long, so I decided to passing data between views than retrieve data from firestore every time.
First View Controller
class HomeTableViewController: UIViewController
// I need to pass this array to another view
var workoutsName: [String] = []
...
func sendDataToCalendar() {
// IN THIS FUNCTION I RETRIEVE DATA FROM FIRESTORE AND UPDATE ARRAY WITH VALUES
// After all I print this array to check if everything is correct, and my data is here
print("\(workoutsName)")
}
Here is my Second View Controller when I want to use array from First View Controller
class CalendarViewController: UIViewController {
var arrayOfTitles = [String]()
.
.
.
func getArrayFromHome() {
let homeVC = HomeTableViewController()
homeVC.workoutsName = arrayOfTitles
// NOW I PRINT TO CHECK IF DATA EXISTS
print("\(arrayofTitles)"
}
And its empty, so data didn't pass.
Maybe it's not the best way to pass data, but main idea of this is that I will need to use this array in few view controllers. These controllers won't be connected by segue, so I can't use prepareforSegue. It's not one to one relationship (in future I will need this array in few controllers), so I shouldn't use delegate. I don't think that notification will be ok too, so I think that it's best option to pass data by property. But maybe I am wrong, so please correct me.
The reason why it doesn't work is that you instantiate a new HomeTableViewController with empty data.
If this data will be used on lots of place, why not save it locally? user default seems like it fit your needs.
func sendDataToCalendar() {
UserDefaults.standard.set(workoutsName, forKey: "workoutsName")
}
Then you can read it later on
func getWorkoutNameArray() {
let workoutName = UserDefaults.standard.object(forKey: "workoutsName") as? [String]
}
In your getArrayFromHome() function you are not accessing HomeTableViewController instance where you got the data but creating a new instance. That's why the array is empty. You end up with 2 instances of HomeTableViewController, one in use with the correct array and the dummy one created in the function with an empty array.
Would be better if you pass the data in the same place where you have a reference to CalendarViewController.
Let's say that you are creating and presenting CalendarViewController in your HomeTableViewController like:
let calendarViewController = CalendarViewController()
calendarViewController.arrayOfTitles = workoutNames
// Here is the presentation or push of calendarViewController
It will be useful for you to read this SO question

Load UITable Data from another View Controller

I have two Views:
UITableViewController (View A)
UIViewController (View B)
I was wondering, if it's possible to load and setup the table from View B and then segue to View A, when the loading is done. I need this, since the Table View loads Data from Core Data and that takes some time; I would then show a Loading Animation or something. I have a function called loadData() in View A, which fetches all Elements from Core Data and then calls tableView.reloadData().
Does anyone know, how I could implement this? Or should I somehow show the loading View directly from View A with a SubView or something?
Remember to not think about the specifics but instead, think generally:
You want to move from one VC to another and you have some data that needs to be fetched asynchronically. Let's assume you can't know how long it will take.
My suggestion is to contain all data fetching related to a VC inside that VC itself (or services/facades related to it). So basically you should present the UITableViewController and then have it fetch the data while showing skeleton-cells/spinner/etc.
You want to have separation of concerns which means you don't want your ViewController to handle data related to another view controller.
Think about the following use-case: if you have code to fetch data in the previous VC, before presenting the TVC, what happens when you need to re-fetch the data or refresh something? You will have to duplicate the code in both the VC and the TVC.
That's why it's suggested to keep data fetching inside the view controller that needs it.
If, for some reason, you still want to have your answer for this specific question:
You can have the initial VC create the TVC, but not present it yet, call its methods to fetch the data, and have it send a callback (closure/delegate/etc) when it's done fetching. When the fetching is done, present the TVC.
Here is a quick example:
class MyTableVC: UITableViewController {
private var myData: [Int] = []
public func fetchData(completion: () -> Void) {
//Fetch data asyncly
myData = [1, 2 ,3]
completion()
}
}
class MyVC: ViewController {
private func loadTableVC() {
let tableVC = MyTableVC()
tableVC.fetchData { [weak self] in
self?.present(tableVC, animated: true, completion: nil)
}
}
}
Again, I wouldn't use this due to having tight coupling between the 2 view controllers, but it's always up to you to decide how to design your code.

sorting UITableview rows in a UI Segmented Controller

I am using a Segmented controller to control how data is presented in my table view. One of the views should be chronologically which is easy because I use firebase so the query is effectively already chronological. However, I want to sort by highest rated which is a computed value (the "number of likes" is the count of a child which is an array of users who clicked "like". the table view controller instantiates an array:
var media = [Media]()
and a function fetches the media and ends like this:
let ref = DatabaseReference.media.reference()
let query = ref.queryOrdered(byChild: "uid").queryEqual(toValue: (value))
query.observe(.childAdded, with: { snapshot in
let media = Media(dictionary: snapshot.value as! [String : Any])
if !self.media.contains(media) {
self.media.insert(media, at: 0)
I think I need to make 2 different arrays and use a switch statement on segmented controller before the last 2 lines to populate them, then use switch statements in my TableView Data Source to read the associated arrays. Is this the right idea? If so, how do I sort snapshots?

Firebase filling array twice -- Swift

I have two String arrays: one that holds order numbers, and one that holds addresses.
I pull data from Firebase in viewDidAppear using a function that contains the following:
self.localOrderNumberArray.removeAll()
self.localAddressArray.removeAll()
self.orderNumbers.removeAll()
self.addresses.removeAll()
self.tableView.reloadData()
if onItsWayCompanyNameStoreNumberCourierNumberRootRef != nil {
let deliveryRef = onItsWayCompanyNameStoreNumberCourierNumberRootRef.childByAppendingPath("deliveries")
deliveryRef.observeEventType(.ChildAdded, withBlock: { snapshot in
self.orderNumbers.removeAll()
self.addresses.removeAll()
print(snapshot.value.objectForKey("orderNumber"))
let orderNumberPulledFromFirebase = snapshot.value.objectForKey("orderNumber") as! String
self.localOrderNumberArray.insert(orderNumberPulledFromFirebase, atIndex: 0)
let addressPulledFromFirebase = snapshot.value.objectForKey("address") as! String
self.localAddressArray.insert(addressPulledFromFirebase, atIndex: 0)
self.orderNumbers = self.localOrderNumberArray
self.addresses = self.localAddressArray
self.tableView.reloadData()
})
}
The function fills a UITableView with the data pulled from Firebase.
Everything works great when I first run the app. I can add data to Firebase through a different function, and the function above will pull the new data into the UITableView just fine.
However, when I segue to a different view controller (another UITableView, in this case), and then come back to the view that holds the function above, the function fills the order number and address arrays twice when I add new data.
If I segue to the other UITableView a second time, and then come back to view that holds the function above, the function fills the order number and address arrays three times when I add new data. And so on and so on.
It's the strangest thing. I can't figure it out, and it's about to drive me over the edge. Please help.
You are calling deliveryRef.observeEventType in viewDidAppear. viewDidAppear will be called each time the ViewController is presented. So when you segue to other ViewController and comes back, viewDidAppear will be called again and deliveryRef.observeEventType is registered again. So effectively there are two listeners doing the same job in your viewController which will add duplicate data to the array.
You have to implement a logic to do observeEventType only once in the ViewController.

Display a particular user and all their information stored on parse on a different View Controller in Swift

In my app each user sets up a profile page and all this info is stored on Parse. When the current user hits a button it segues to a tableViewController that displays in cells all the other users on the app. In each cell there is a button which the user presses to view the full profile page of the user that they have selected. I have a VC set up to hold all the info I just don't know how to display the particular user that the current user has selected into it. How do I single out the chosen users information? I have read the Parse documentation on relational queries but I can't seem to get my head around it. https://parse.com/docs/ios/guide#queries-relational-queries
So in finer detail basically all my users are displaying on a VC. It has name and profile picture, when the user clicks on the info button beside a certain user it should open a new screen and display all of this users information stored on Parse.
I have tried creating a static outside the class
struct Data { var OtherName:String! var id:String! }
And then in my ViewDidLoad:
let query = PFQuery(className: "_User")
// otherQuery.orderByDescending("createdAt")
query.whereKey("username", notEqualTo:PFUser.currentUser()!.username!)
query.findObjectsInBackgroundWithBlock { (users: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// success
print(users!.count)
for user in users! {
self.imageFiles.append(user["image"] as! PFFile)
self.instrumentText.append(user["instrument"] as! String)
self.nameText.append(user["name"] as! String)
self.ageText.append(user["age"] as! String)
// self.locationText.append(user["location"] as! PFGeoPoint)
var singleData = Data()
singleData.id = user.objectId
singleData.OtherName = user["name"] as! String
print("\(singleData)")
} // users
Then on the VC that the info button segues to I am trying to call it and upload each label with just the user that was selected.
self.userName.text = "" as String
But the "" is staying blank.
Then I was thinking maybe if I add that when the more info button is pressed that the user it was pressed on gets chosen and is added to a chosen string on Parse and then I just have to call the user from the chosen string when I am on the chosen users profile page and call the info that way and then when the user clicks the back button the user is no longer chosen. It just seems like I would be adding and taking away a lot of users to "chosen" in Parse is there a better way?
I am able to display all the users info but how do I just display a chosen user from Parse without using the objectId because at the top of each cell it will return a different user here each time depending on settings previously set on a different VC such as only show users in a certain age bracket etc. I know I must set an Id of some sort on button touched but I am at a complete loss. The only online help out there that I can find is set for other languages eg. https://parse.com/docs/js/guide#users-querying>
So I guess my question is :
1.Does anyone know a tutorial they could point me towards.
2.Does anyone know how I even get started and I can go from there myself.
3.Can anyone help?
*Swift and Parse newbie little to no experience in other languages.
New code in firstVC:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "userProfileDetailsSegue" {
if let indexPath = resultsPageTableView.indexPathForSelectedRow {
let controller = (segue.destinationViewController) as! UserProfileDetailsViewController
***error here: UserProfileDetailsViewController.userToShowDetail = indexPath.row
//line above is the one I can't get to work it needs to equal to the cell selected?
}
}
}
New Code in destinationVC:
var userToShowDetail: PFUser? {
didSet {
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let userToShowDetail: PFUser = self.userToShowDetail {
if let label = self.userName {
nameText.append(userToShowDetail["name"] as! String)
// self.nameText.append(user["name"] as! String)
}
}
}
You're going to need to do a couple of things for this to work. First, in the view controller that you're using to show another user's information, you need to set a property like var userToShowDetail: PFUser?. Then in your view with all of the users of your app, you need to create a segue to bring them from the current view to the detail view. Assuming you're using a TableView, you can connect just one prototype cell to the detail view you're trying to navigate. Then you'll need to override prepareForSegue and set the new view controller's user equal to the user at the row that was selected. I don't know how you're storing your objects of user's of your app, but you should probably be able to do something like this.
override fun prepareForSegue(segue: UIStoryboardSegue) {
if segue.identifer == "segueToShowDetailInfo" {
//get the index path for the row that was selected
let indexPath = self.tableView.indexPathForSelectedRow()
//get the PFUser object for that particular row
let userToShow = self.appUsers[indexPath.row]
//create your new view controller
let newVc = segue.destinationViewController as! DestinationVC
//assign the new vc's property to your object
newVc.userToShowDetail = userToShow
}
}
But in your query, you probably shouldn't have a separate array of the the different parts of the user's data. You should just make one array, call it var appUsers = [PFUser]() and then when you get a user back in your query, just say:
for user in users! {
self.appUsers.append(user) as! PFUser
}
There, now you have a whole array of objects that holds a reference to every user. When someone is using your app, and selects a cell, then you can perform the prepare for segue method as above.
Note that I also noticed you have a didSet on your variable in your new view controller. Calling a function to update the UI after this variable is set will not work, and may crash, because the view controller doesn't have a window yet.

Resources