Related VieWController should show Label from Cell - ios

I try to show the Label from a Cell in a new ViewController when you tap on the cell, so it should be a function like in twitter
I tried following code, but the label just don't show the message from the cell, its just blank...
var jobs = [Job]()
let jobsRef = Database.database().reference().child("jobs")
var job: Job! {
didSet {
jobLabel.text = job.text
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// download jobs
jobsRef.observe(.value, with: { (snapshot) in
self.jobs.removeAll()
for child in snapshot.children {
let childSnapshot = child as! DataSnapshot
let job = Job(snapshot: childSnapshot)
print(job)
self.jobs.insert(job, at: 0)
}
self.tableView.reloadData()
})
}

Use below code
self.jobs.removeAll()
ref.observe(.childAdded, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : AnyObject] else {
return
}
let Obj = Job()
self.jobs.append(Obj)
self. tableView.reloadData()
}, withCancel: nil)

Every UIViewController that will appear within the PagerTabStrip needs to provide either a title or an image. Discord In order to do so they should conform to Indicator InfoProvider Adobe Reader by implementing func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo which provides the information required to show the PagerTabStrip menu (indicator) associated with the view controller iTunes.

Related

How to observe more levels from realtime-database Firebase?

My code do not add values from a while statement of a UIViewController to an Array of a UITableViewController.
This is for a getter function to allow me to see all childrens values under other childrens. Now I'm going to be more specific:
My database node is made of:
Cars -> 0, 1, 2, 3, ... -> Model, Price, ... -> String
As you can see, The number of childs is undefined, so I have to use this control method:
while let child = snapshotChildren.nextObject() as? DataSnapshot {
// Get code node key and save it to cars array
}
First of all, In a loading ViewController, I get code node keys of cars and save them to cars variable of type NSMutableArray of the TableViewController. Then I will do the same thing in the TableViewController to get all indexpath.row childrens value.
let rootRef = Database.database().reference()
let carconditionalRef = rootRef.child("Cars")
carconditionalRef.observe(.value) {(snap: DataSnapshot) in
//Get all the children from snapshot you got back from Firebase
let snapshotChildren = snap.children
//Loop over all children (code) in Firebase
while let child = snapshotChildren.nextObject() as? DataSnapshot {
// Get code node key and save it to cars array
let carvc = Cars_Table();
carvc.cars.add(child.key)
}
}
It results that with this code I still have empty NSMutableArray. How can I solve this?
Edit 1
I fixed that snippet to this:
import UIKit
import FirebaseDatabase
class Loading: UIViewController {
#IBOutlet weak var loading: UIActivityIndicatorView!
var mother: NSMutableArray = []
override func viewDidLoad() {
super.viewDidLoad()
start()
}
func start() {
loading.startAnimating()
if #available(iOS 10.0, *) {
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in
//let's dance
self.loading.startAnimating()
//call data from database
let rootRef = Database.database().reference()
let conditionalRef = rootRef.child("Cars")
conditionalRef.observe(.value) {(snap: DataSnapshot) in
// Get all the children from snapshot you got back from Firebase
let snapshotChildren = snap.children
// Loop over all children (code) in Firebase
while let child = snapshotChildren.nextObject() as? DataSnapshot {
// Get code node key and save it to cars array
self.mother.add(child.key)
}
self.move()
self.loading.stopAnimating()
self.performSegue(withIdentifier: "loadingfinish", sender: nil)
}
}
} else {
// Fallback on earlier versions
}
}
func move() {
let vc = Cars_Table()
vc.cars = self.mother
}
}
Edit 2
I tried using the recursive method, but it did not work. So I tried one more time with the iterative method this time using the while statement.
Here my new function, this time directly in the Car_TableView.swift:
func loadData() {
//call data from database
let rootRef = Database.database().reference()
let conditionalRef = rootRef.child("Cars")
conditionalRef.observe(.value) {(snap: DataSnapshot) in
// Get all the children from snapshot you got back from Firebase
let snapshotChildren = snap.children
// Loop over all children (code) in Firebase
while let child = snapshotChildren.nextObject() as? DataSnapshot {
// Get code node key and save it to cars array
self.populateTable.append(child.key)
}
var counter = 0
while counter > -self.populateTable.count {
counter -= 1
let rootRef = Database.database().reference()
let userRef = rootRef.child("Cars").child("\(self.populateTable.count+counter)")
userRef.observeSingleEvent(of: .value, with: { snapshot in
let userDict = snapshot.value as! [String: Any]
let model1 = userDict["Model"] as! String
self.model.add(model1)
let detail1 = userDict["Detail"] as! String
self.detailpage.add(detail1)
let year1 = userDict["Year"] as! String
self.year.add(year1)
let carPrice1 = userDict["Price"] as! String
self.price.add(carPrice1)
let carimageURL1 = userDict["imageURL"] as! String
self.imagePathString.add(carimageURL1)
}) //end observeSingleEvent
}
}
}
When I go to do the while, the observeSingleEvent will be work, but it will repeat n^2 times. Why does this happen?
Edit 3
Since the problem seems to be changed since the start, I edited to give all the relevant details. So, the problem now is different and now are two:
When I load database I have n^2 repeated instruction
To see the table filled with database data, I have to touch the tab bar button to the next ViewController then touch the tab bar button to come back on Car_TableView.swift
For the first problem... onestly I have no idea why this happens 😅
For the second problem I thought to use SVProgressHUD to reload data but it doesn't work on loadData() function and if I try the Instance Method tableView.reloadData() it crashes.
variables are all NSMutableArray since that I have to load a lot of stuff that can change in the time
My viewDidLoad() function is very easy as you can see:
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
This is my Table view data source in our Car_TableView.swift:
override func numberOfSections(in tableView: UITableView) -> Int {
return populateTable.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return populateTable.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "carTableCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! Car_Cell
cell.carLabel?.text = "\(self.model[indexPath.row])"
cell.carSubtitle?.text = "Year: \(self.year[indexPath.row]) - Price: \(self.price[indexPath.row])$"
Alamofire.request("\(self.imagePathString[indexPath.row])").response { response in
guard let image = UIImage(data:response.data!) else {
// Handle error
return
}
let imageData = image.jpegData(compressionQuality: 1.0)
cell.carImage.contentMode = .scaleAspectFit
cell.carImage.image = UIImage(data : imageData!)
}
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowcarDetails" {
let myIndexPath = self.tableView.indexPathForSelectedRow!
//save detail page url in UserDefault
let SVDetail = self.detailpage[myIndexPath.row]
let SVDetaildefaults = UserDefaults.standard
SVDetaildefaults.set(SVDetail, forKey: "sv_detail")
SVDetaildefaults.synchronize()
_ = segue.destination
as! Car_Detail
}
}
//SET CELLS SIZE
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch indexPath.row {
case 0,1,2,3,4:
return 100
default:
return 100
}
}
I also re-post the loadData() function because I simplified the operation in an only while statement:
func loadData() {
//call data from database
let rootRef = Database.database().reference()
let conditionalRef = rootRef.child("Cars")
conditionalRef.observe(.value) {(snap: DataSnapshot) in
// Get all the children from snapshot you got back from Firebase
let snapshotChildren = snap.children
// Loop over all children (code) in Firebase
while let child = snapshotChildren.nextObject() as? DataSnapshot {
// Get code node key and save it to cars array
self. populateTable.append(child.key)
let userRef = rootRef.child("Cars").child("\(child.key)")
userRef.observeSingleEvent(of: .value, with: { snapshot in
let userDict = snapshot.value as! [String: Any]
let address1 = userDict["Address"] as! String
self.address.add(address1)
let detail1 = userDict["Detail"] as! String
self.detailpage.add(detail1)
let carnumberOfRooms1 = userDict["numberOfRooms"] as! String
self.numberOfRooms.add(carnumberOfRooms1)
let carPrice1 = userDict["Price"] as! String
self.price.add(carPrice1)
let carimageURL1 = userDict["imageURL"] as! String
self.imagePathString.add(carimageURL1)
}) //end observeSingleEvent
} //end while
} //end snap
}//end func

Call reloadData() in the right place

I'm trying to fetch data from firebase and pass to tableview.
// Model
import UIKit
import Firebase
struct ProfInfo {
var key: String
var url: String
var name: String
init(snapshot:DataSnapshot) {
key = snapshot.key
url = (snapshot.value as! NSDictionary)["profileUrl"] as? String ?? ""
name = (snapshot.value as! NSDictionary)["tweetName"] as? String ?? ""
}
}
// fetch
var profInfo = [ProfInfo]()
func fetchUid(){
guard let uid = Auth.auth().currentUser?.uid else{ return }
ref.child("following").child(uid).observe(.value, with: { (snapshot) in
guard let snap = snapshot.value as? [String:Any] else { return }
snap.forEach({ (key,_) in
self.fetchProf(key: key)
})
}, withCancel: nil)
}
func fetchProf(key: String){
var outcome = [ProfInfo]()
ref.child("Profiles").child(key).observe(.value, with: { (snapshot) in
let info = ProfInfo(snapshot: snapshot)
outcome.append(info)
self.profInfo = outcome
self.tableView.reloadData()
}, withCancel: nil)
}
//tableview
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return profInfo.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "followCell", for: indexPath) as! FollowingTableViewCell
cell.configCell(profInfo: profInfo[indexPath.row])
return cell
}
However it returns one row but profInfo actually has two rows. when I implement print(self.profInfo) inside fetchProf it returns two values. But after passed to tableview, it became one. I'm not sure but I guess the reason is that I put reloadData() in the wrong place because I hit break point and reloadData() called twice. So, I think profInfo replaced by new value. I called in different places but didn't work. Am I correct? If so, where should I call reloadData()? If I'm wrong, how can I fix this? Thank you in advance!
You need to append the new data to the profinfo array. Simply replace the fetchProf method with this:-
func fetchProf(key: String){
var outcome = [ProfInfo]()
ref.child("Profiles").child(key).observe(.value, with: { (snapshot) in
let info = ProfInfo(snapshot: snapshot)
outcome.append(info)
self.profInfo.append(contentOf: outcome)
Dispatch.main.async{
self.tableView.reloadData()
}
} , withCancel: nil)
}
self.tableView.reloadData() must be called from the main queue. Try
DispatchQueue.main.async {
self.tableView.reloadData()
}
if you notice one thing in the following function you will see
func fetchProf(key: String){
var outcome = [ProfInfo]()
ref.child("Profiles").child(key).observe(.value, with: { (snapshot) in
let info = ProfInfo(snapshot: snapshot)
outcome.append(info)
//Here
/You are replacing value in self.profInfo
//for the first time when this is called it results In First profile info
//When you reload here first Profile will be shown
//Second time when it is called you again here replaced self.profInfo
//with second Outcome i.e TableView reloads and output shown is only second Profile
//you had initialised a Array self.profInfo = [ProfInfo]()
//But you just replacing array with Single value Actually you need to append data
// I think here is main issue
self.profInfo = outcome
//So try Appending data as
//self.profInfo.append(outcome) instead of self.profInfo = outcome
//Then reload TableView to get both outputs
self.tableView.reloadData()
}, withCancel: nil)
}
Table view showing one content because when table view reloaded then profile info not combine all data. You need to reload the table view after combining all data. This will help you.
// fetch
var profInfo = [ProfInfo]()
func fetchUid(){
guard let uid = Auth.auth().currentUser?.uid else{ return }
ref.child("following").child(uid).observe(.value, with: { (snapshot) in
guard let snap = snapshot.value as? [String:Any] else { return }
snap.forEach({ (key,_) in
self.fetchProf(key: key)
})
// When all key fetched completed the just reload the table view in the Main queue
Dispatch.main.async{
self.tableView.reloadData()
}
}, withCancel: nil)
}
func fetchProf(key: String){
ref.child("Profiles").child(key).observe(.value, with: { (snapshot) in
let info = ProfInfo(snapshot: snapshot)
self.profInfo.append(info) // Here just add the outcome object to profileinfo
}, withCancel: nil)
}
This way no need to handle another array.

Updating tableview without reloading the view (Swift, Firebase)

So I am trying to get all of the favourites from a database to show up in a tableview. However, when I favourite a user, it only displays one.
The structure of my database looks like:
Favourites:
UserID:
key: User 1
key: User 2
and my code:
var usersArray[UserClass]()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
fetchFavourites()
}
func fetchFavourites () {
let userID = Auth.auth().currentUser?.uid
let favouriteRef = self.databaseRef.child("favourites").child(userID!)
favouriteRef.queryOrderedByKey().observe(.childAdded, with: { (snapshot) in
let favouriteID = "\(snapshot.value!)"
let usersRef = self.databaseRef.child("users")
usersRef.observe(.value, with: { (users) in
self.usersArray.removeAll()
for user in users.children {
let user = UserClass(snapshot: user as! DataSnapshot)
if favouriteID == user.uid {
self.usersArray.append(user)
}
self.tableView.reloadData()
}
})
})
}
I have tried creating another array within the function and then making the main array = to that, however it doesn't update the tableview when switching between screens. This code only works when the page view is loaded for the first time. However, I am using a back button that doesn't up date the screen.
Thanks, let me know if you want me to provide anything else.
Edited code (new problem, see below):
func fetchFavourites () {
DispatchQueue.main.async( execute: {
let userID = Auth.auth().currentUser?.uid
var tempFav = [UserClass]()
let favouriteRef = self.databaseRef.child("favourites").child(userID!)
favouriteRef.queryOrderedByKey().observe(.childAdded, with: { (snapshot) in
let favouriteID = "\(snapshot.value!)"
let usersRef = self.databaseRef.child("users")
tempFav.removeAll()
usersRef.observe(.value, with: { (users) in
self.usersArray.removeAll()
for user in users.children {
let user = UserClass(snapshot: user as! DataSnapshot)
if favouriteID == user.uid {
tempFav.append(user)
}
self.usersArray = tempFav
self.tableView.reloadData()
}
})
})
})
}
The problem has been fixed where it populates the tableview with all of the liked users and gets rid of them if I unlike them etc. However, if I unlike all of the users. The array duplicates a user twice even though it should be empty.

Completion handler Firebase observer in Swift

I am making a completion handler for a function which will return a list of objects. When it return value for first time, it works well. But when any change happen into firebase database and again observe gets called, array size gets doubled up. Why it's getting doubled up?
func getStadiums(complition: #escaping ([Stadium]) -> Void){
var stadiums: [Stadium] = []
let stadiumRef = Database.database().reference().child("Stadium")
stadiumRef.observe(.value, with: { (snapshot) in
for snap in snapshot.children {
guard let stadiumSnap = snap as? DataSnapshot else {
print("Something wrong with Firebase DataSnapshot")
complition(stadiums)
return
}
let stadium = Stadium(snap: stadiumSnap)
stadiums.append(stadium)
}
complition(stadiums)
})
}
And calling like this
getStadiums(){ stadiums
print(stadiums.count) // count gets doubled up after every observe call
}
The code you're using declares stadiums outside of the observer. This means any time a change is made to the value of the database reference, you're appending the data onto stadiums without clearing what was there before. Make sure to remove the data from stadiums before appending the snapshots again:
func getStadiums(complition: #escaping ([Stadium]) -> Void){
var stadiums: [Stadium] = []
let stadiumRef = Database.database().reference().child("Stadium")
stadiumRef.observe(.value, with: { (snapshot) in
stadiums.removeAll() // start with an empty array
for snap in snapshot.children {
guard let stadiumSnap = snap as? DataSnapshot else {
print("Something wrong with Firebase DataSnapshot")
complition(stadiums)
return
}
let stadium = Stadium(snap: stadiumSnap)
stadiums.append(stadium)
}
complition(stadiums)
})
}
This line stadiumRef.observe(.value, with: { (snapshot) in ... actually adding an observer that will be called everytime your stadium data is changed.
Because you called it twice by using getStadiums(){ stadiums ..., the total observer added will be 2.
That makes the line stadiums.append(stadium) called twice in the second call.
My suggestion would be to use stadiumRef.observe() once without calling it from getStadiums().
Create a Model as below
class OrderListModel: NSObject {
var Order:String?
var Date:String?
}
Use the below code in the view controller and you should be able to see content in your tableview
func getOrdersData() {
self.orderListArr.removeAll()
let ref = Database.database().reference().child(“users”).child(user).child("Orders")
ref.observe(.childAdded, with: { (snapshot) in
print(snapshot)
guard let dictionary = snapshot.value as? [String : AnyObject] else {
return
}
let orderObj = OrderModel()
orderObj.Order = dictionary[“Order”] as? String
orderObj.Date = dictionary[“Date”] as? String
self.orderListArr.append(orderObj)
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.reloadData()
}, withCancel: nil)
}
func ListenForChildrenAdded() {
let registerToListenTo = "YourPathHere"
ref.child(registerToListenTo).observeSingleEvent(of: .value) { (snapshot) in
let initialChildren = snapshot.childrenCount
var incrementer = 0
ref.child(registerToListenTo).observe(.childAdded, with: { (snapshot) in
incrementer += 1
print("snapshot: \(snapshot.key) #\(incrementer)")
if incrementer == initialChildren {
print("-> All children found")
} else if incrementer > initialChildren {
print("-> Child Was Added - Run Some Code Here")
}
})
}}

Completion handler in refreshControl has EXC_BAD_ACCESS error

I am using Firebase as my data structure. I use completion handler in my UITableView's refreshControl, in order to stop refreshing when finish loading all data from Firebase.
override func viewDidLoad() {
super.viewDidLoad()
self.refreshControl = UIRefreshControl()
self.refreshControl!.addTarget(self, action: #selector(refreshData),for: .valueChanged)
self.refreshControl!.attributedTitle = NSAttributedString(string: "Update the data")
refreshData{ _ in
self.refreshControl!.endRefreshing()
}
}
And this is my refreshData method
func refreshData(completionHandler:#escaping (Bool)->() ) {
//Remove old data
self.items.removeAll()
//Renew all data
var ref: DatabaseReference!
ref = Database.database().reference(withPath: "tasks")
//Loading local drafts
var drafts : [Task]!
if let local_drafts = NSKeyedUnarchiver.unarchiveObject(withFile: Task.ArchiveURL.path) as? [Task] {
drafts = local_drafts
}
else{
drafts = []
}
//Reloading the database
ref.observe(.value, with: { snapshot in
var newItems: [Task] = []
self.num_of_tasks = Int(snapshot.childrenCount)
for item in snapshot.children {
//let local = item as! DataSnapshot
//let snapshotValue = local.value as! [String: AnyObject]
//print(snapshotValue["main_content"] as! String!)
let taskItem = Task(snapshot: item as! DataSnapshot)
newItems.append(taskItem!)
}
let merged = drafts + newItems
self.items = merged
completionHandler(true) //THIS LINE HAS ERR_BAD_ACCESS
})
}
I think the problem might be the two refreshData in viewDidLoad. But I don't know how to fix it. How could I add refreshData with handler as a selector?
This is not a thread problem. I solve it by wrapping the function call in another function because in this line
self.refreshControl!.addTarget(self, action: #selector(refreshData),for: .valueChanged)
I tried to use refreshData as a selector, but actually, selector doesn't have any completion handler by itself, so that caused the memory error whenever the user tried to renew by drag the scene down. So by wrapping the function call in another function
func refresh(){
refreshData{ [weak self] _ in
if let _ = self {
self?.refreshControl!.endRefreshing()
}
}
}
and use this function in the selector, it will deliver the right completion handler, and thus solve the problem.

Resources