Swift: Retrieve data from Firebase Database to label - swift

I'm trying to get my data from Firebase Database to particular label in Swift. I have two labels in TableView (as Main.storyboard) tagged 1 and 2.
In a ViewController, I have this code:
import UIKit
import Firebase
import FirebaseDatabase
struct confStruct {
let title : String!
let place : String!
}
class EVS_Table_VC: UITableViewController {
var conf = [confStruct]()
override func viewDidLoad() {
super.viewDidLoad()
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("conferences").queryOrderedByKey().observeEventType(.ChildAdded, withBlock: {
snapshot in
self.tableView.reloadData()
})
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return conf.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = conf[indexPath.row].title
let label2 = cell?.viewWithTag(2) as! UILabel
label2.text = conf[indexPath.row].place
return cell!
}
}
But nothing shows up in a Simulator. Does somebody have a proposition how to resolve this? This struct confStruct initializes my variable from
Database? (title, place).
JSON tree:
"conferences": {
"Key": {
"date": "some date"
"deadline": "some deadline"
"place": "some place"
"title": "some title"
}
}

Change your struct to :-
struct confStruct {
let title : String!
let place : String!
init(title_String : String!, place_String : String!){
self.title = title_String
self.place = place_String
}
}
And:-
FIRDatabase.database().reference().child("conferences/Key").observeSingleEvent(of: .value, with: {(snap) in
if let snapDict = snap.value as? [String:AnyObject]{
let titleS = snapDict["title"] as! String
let placeS = snapDict["place"] as! String
let temp = confStruct.init(title_String: titleS, place_String: placeS)
self. conf.append(temp)
self.tableView.reloadData()
}
})

Related

Reloading cells in view after image downloaded

I am currently learning swift. I have experience in android but now time for something new. I am starting with basics to load movie DB from API to table. I am storing dowloaded poster in Movie class (which also downloads them) when scrolling I can see the posters but after download the current cells in the view not updated, only after scroll. How can I implement callback from Movie to table view to update visible cells after download.
Movie:
import UIKit
let URL_PREFIX = "https://image.tmdb.org/t/p/original"
class Movie {
let movieId: CLong?
let title: String?
let posterPath: String?
let overview: String?
let releaseDate: String?
var posterImage: UIImage?
var callback: ((_ id: Int) -> Void)?
init(movieId: CLong,title: String,posterPath: String,overview: String,releaseDate: String,posterImage: UIImage?=nil) {
self.movieId = movieId
self.title = title
self.posterPath = posterPath
self.overview = overview
self.releaseDate = releaseDate
self.posterImage = posterImage
setResizedImage(path: posterPath)
}
func setResizedImage(path: String)
{
let conPath = URL_PREFIX + path
print("Path: \(conPath)")
guard let urlPath = URL(string: conPath) else {
print("You fucked up")
return
}
print("Download Started")
getData(from: urlPath) { data, response, error in
guard let _ = data, error == nil else { return }
print(response?.suggestedFilename ?? urlPath.lastPathComponent)
print("Download Finished")
//update
DispatchQueue.main.async()
{
self.posterImage = UIImage(data: data!)
}
}
}
}
MyViewController:
import UIKit
let URL_MOVIES = "https://api.themoviedb.org/3/movie/upcoming?
api_key=000";
class DataViewController: UIViewController,UITableViewDelegate,
UITableViewDataSource {
#IBOutlet weak var myTable: UITableView!
var movieArray :[Movie] = []
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movieArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "MovieTableViewCell", for: indexPath) as? CustomTableViewCell else {
fatalError("The dequeued cell is not an instance of MovieTableViewCell.")
}
let movie = movieArray[indexPath.row]
cell.title.text = movie.title
cell.releaseDate.text = movie.releaseDate
cell.overview.text = movie.overview
//cell.url.text = movie.overview
if (movie.posterImage==nil)
{
print("Loaded placeholder")
cell.poster.image = UIImage(named: "poster")
}
else
{
print("Hello2")
cell.poster.image = movie.posterImage
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
override func viewDidLoad() {
super.viewDidLoad()
myTable.rowHeight = UITableView.automaticDimension
myTable.estimatedRowHeight = 50
getJsonFromUrl()
}
func getJsonFromUrl(){
let url = NSURL(string: URL_MOVIES)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
if let resultArray = jsonObj.value(forKey: "results") as? NSArray
{
for film in resultArray
{
if let movieDict = film as? NSDictionary
{
//getting the name from the dictionary
let id = movieDict.value(forKey: "id")
let title = movieDict.value(forKey: "title")
let posterPath = movieDict.value(forKey: "poster_path")
let overview = movieDict.value(forKey: "overview")
let releaseDate = movieDict.value(forKey: "release_date")
let movie = Movie(movieId:id as! CLong, title: title as! String, posterPath: posterPath as! String, overview: overview as! String, releaseDate: releaseDate as! String)
self.movieArray.append(movie)
}
}
}
OperationQueue.main.addOperation({
self.myTable.reloadData()
})
}
}).resume()
}
}
You can add the download function inside the cell custom class and assign the imageView inside the callback, but this has many problems such as redownloading same image multiple times when scrolling, it's better to use SDWebImage or you can use Kingfisher Library
import SDWebImage
cell.poster.sd_setImage(with: URL(string:movie.imageUrlStr), placeholderImage: UIImage(named: "placeholder.png"))

Data is not displayed in TableView from Firebase

I have a 2 problems with displaying data in a table from Firebase.
Nothing displayed in TableView from Firebase
I I can not add a link(child) to a variable
Print is working. I get access to Firebase, but nothing is added to TableView. Please, look at my code and correct where i'm wrong.
It's my model
class Exercises {
var titleExercise = ""
var descriptionExercise = ""
init (titleExercise: String, descriptionExercise: String) {
self.titleExercise = titleExercise
self.descriptionExercise = descriptionExercise
}
}
It's my ViewController
class ExercisesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//MARK: Properties
var refWorkout: String = ""
var workout: TrainingProgram?
var ref: DatabaseReference!
#IBOutlet weak var tableView: UITableView!
var exercises = [Exercises]()
//MARK: Methods
override func viewDidLoad() {
super.viewDidLoad()
fetchExercises()
tableView.dataSource = self
tableView.delegate = self
refWorkout = workout!.title
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return exercises.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ExercisesTableViewCell
let workouts = exercises[indexPath.item]
cell.titleLabel.text = workouts.titleExercise
cell.descriptionLabel.text = workouts.descriptionExercise
return cell
}
func fetchExercises() {
Database.database().reference().child("programs").child("OPEN SPACE").child("exercises").observe(.childAdded) { (snapshot) in
print(snapshot.value)
if let dict = snapshot.value as? [String: AnyObject] {
let newTitle = dict["title"] as! String
let newDescription = dict["description"] as! String
let exerciseTableCell = Exercises(titleExercise: newTitle, descriptionExercise: newDescription)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
And I have second question. It also addresses this issue.
As you can see, I have refWorkout = workout!.title Here comes the title from previous ViewController , and refWorkout is a child for Firebase. If I will write next code
ref = Database.database().reference().child("programs").child(refWorkout).child("exercises")
ref.observe(.childAdded) { (snapshot) in
print(snapshot.value)
}
Everything will work. Print will work. But if I insert this code to func fetchExercises() - > It will look like
func fetchExercises() {
Database.database().reference().child("programs").child(refWorkout).child("exercises").observe(.childAdded)...
My app crashed.
Please help me with two questions. Thank you!
My Firebase structure
This is a common mistake, you are reloading the table view too soon and you don't assign/append the result to the data source array
The observe API works asynchronously, put the line to reload the table view into the closure
func fetchExercises() {
Database.database().reference().child("programs").child("OPEN SPACE").child("exercises").observe(.childAdded) { (snapshot) in
print(snapshot.value)
if let dict = snapshot.value as? [String: Any] { // most likely all values are value type
let newTitle = dict["title"] as! String
let newDescription = dict["description"] as! String
let exercise = Exercises(titleExercise: newTitle, descriptionExercise: newDescription)
self.exercises.append(exercise)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
Side note:
You class contains 3 bad practices:
Semantically objects used in collection types should be named in singular form.
Don't declare properties with default values if there is an initializer.
There is too much redundant information in the variable names
And in most cases a struct and even constants are sufficient. I'd recommend
struct Exercise {
let title : String
let description : String
}
In a struct you get the initializer for free.

Cell in TableView not displaying info. Swift

Cell not displaying anything, but print(dictionary) is displaying correct info. So I am getting the correct information
Here is my code:
import UIKit
import Firebase
import FirebaseDatabase
import FirebaseAnalytics
class VCTableViewController: UITableViewController {
var ref: DatabaseReference!
var refHandle: UInt!
var requestList = [request]()
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
ref = Database.database().reference()
fetchUsers()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for:indexPath)
// let cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellId)
cell.textLabel?.text = requestList[indexPath.row].name
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return requestList.count
}
func fetchUsers() {
refHandle = ref.child("Request").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String : AnyObject] {
print(dictionary)
let request1 = request(dictionary: dictionary)
// request1.setValuesForKeys(dictionary)
self.requestList.append(request1)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
})
}
}
My request swift file:
import Foundation
class request: NSObject {
var request: String?
var name: String?
var longitude: String?
var latitude: String?
init (dictionary: [String: Any]) {
super.init()
request = dictionary["request"] as? String
}
}
Try this:
class request: NSObject {
var request: String?
var name: String?
var longitude: String?
var latitude: String?
init (dictionary: [String: Any]) {
super.init()
name = dictionary["name"] as? String
longitude = dictionary["longitude"] as? String
latitude = dictionary["latitude"] as? String
}
}
But there are better ways to deserialize. Take a look at Codable.
Here's an example that gives you something to think about going forward:
//Codable protocol is for both Encoding & Decoding
struct Location: Codable {
let name: String
let longitude: String
let latitude: String
}
//Encode to json format from struct
let location = Location(name: "my location", longitude: "-94.420307", latitude: "44.968046")
if let encoded = try? JSONEncoder().encode(location) {
if let encodedJSON = String(data: encoded, encoding: .utf8) {
print(encodedJSON)
//Prints: {"name":"my location","longitude":"-94.420307","latitude":"44.968046"}
}
}
//Decode from json data to struct
let jsonStr = """
{
\"name\": \"my location\",
\"longitude\": \"-94.420307\",
\"latitude\": \"44.968046\"
}
"""
//jsonData is of type Data? which is generally what comes back from http request.
if let jsonData = jsonStr.data(using: .utf8) {
if let decoded = try? JSONDecoder().decode(Location.self, from: jsonData) {
print(decoded.name, decoded.longitude, decoded.latitude)
//Prints: "my location -94.420307 44.968046"
}
}

Swift 3 Custom TableViewCell Error

I am having trouble communicating with the "TableView". How can I solve the problem?
Error Image
Code Image
var arrayOfData = [cellData]()
var fullname1 : String!
var code1 : String!
var name1 : String!
var buy1 : Double!
var sell1 : Double!
var change_rate1 : Double!
var update_date1 : Double!
var arrayFullName : [String] = ["asdas","asds","asds","sadas","asdasd","asdas"]
#IBOutlet weak var tblDoviz: UITableView!
#IBOutlet weak var openBarButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
arrayOfData = [cellData(fullName: "Amerikan Doları", name: "", code: "USD", updateDate: 2, changeRate: 2, buy: 3.44, sell: 3.47)]
openBarButton.target = self.revealViewController()
openBarButton.action = Selector("revealToggle:")
tblDoviz.delegate = self
tblDoviz.dataSource = self
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
/* Doviz Sitesinden Bilgiler Çekiliyor */
let url = NSURL(string: "http://www.doviz.com/api/v1/currencies/all/latest")
let task = URLSession.shared.dataTask(with: url as! URL){(data,response,error) ->Void in
if error != nil
{
}
else
{
if let urlContent = data
{
do
{
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let currencyRate = jsonResult as? NSArray
{
for i in 0..<currencyRate.count
{
if let name = (currencyRate[i] as? NSDictionary)?["name"] as? String
{
self.name1 = name
}
if let fullname = (currencyRate[i] as? NSDictionary)?["full_name"] as? String
{
self.fullname1 = fullname
}
if let change_rate = (currencyRate[i] as? NSDictionary)?["change_rate"] as? Double
{
self.change_rate1 = change_rate
}
if let code = (currencyRate[i] as? NSDictionary)?["code"] as? String
{
self.code1 = code
}
if let update_date = (currencyRate[i] as? NSDictionary)?["update_date"] as? Double
{
self.update_date1 = update_date
}
if let buying = (currencyRate[i] as? NSDictionary)?["buying"] as? Double
{
self.buy1 = buying
}
if let selling = (currencyRate[i] as? NSDictionary)?["selling"] as? Double
{
self.sell1 = selling
}
//Array'a Yükleme Yapılıyor
//cellData.init(fullName: self.fullname1, name: self.name1, code: self.code1, updateDate: self.update_date1, changeRate: self.change_rate1, buy: self.buy1, sell: self.sell1)
//self.arrayOfData = [cellData(fullName: self.fullname1, name: self.name1, code : self.code1, updateDate: self.update_date1, changeRate: self.change_rate1, buy: self.buy1, sell: self.sell1)]
//print(self.arrayOfData)
//print(cellData())
}
print(self.arrayFullName.count)
}
}
catch
{
}
}
}
}
task.resume()
//--------------------------------------------------
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellDoviz",for:indexPath) as! DovizCell1
Error-------------------------------------
cell.lblText.text = arrayFullName[indexPath.row]
Error -----------------------------------
return cell as DovizCell1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 94
}
}
Try adding this to your code
let cell:DovizCell? = tableView.dequeueReusableCell(withIdentifier: "cellDoviz",for:indexPath) as! DovizCell
if cell == nil {
cell = NSBundle.mainBundle().loadNibNamed("DovizCell").first as! DovizCell
}
Your DovizCell might have been nil which is why the code crashed

populate UITableView with data stored in Firebase database

I have a UITableVIew in my iPhone application that populates each cell using an array but the question is how do you populate the array with values retrieved from the firebase database:
func retData(){
rootRef.child("users").child("Test").observeEventType(.Value){
(snap: FIRDataSnapshot) in
}
}
var no1 = ["3","6","3","4","5","20","34","34"]
Table Code:
func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell {
let cell1 = self.h_table.dequeueReusableCellWithIdentifier("cell1",
forIndexPath: indexPath) as! Hole_Cell
cell1.s_label.text = usersArray[indexPath.row]
firebase data Structure:
--Users
--Test: 1
--Test1: 2
--Test2: 3
--Test4: 4
Try this:
var usersArray: [String]?
func retData() {
rootRef.child("users").observeEventType(.Value, withBlock: { snapshot in
usersArray = [String]()
for user in snapshot.allObjects as! [FIRDataSnapshot] {
let userString = user.value as? String
usersArray?.append(userString!)
}
})
}
***Edit Try this:
var usersArray = [String]()
func retData() {
rootRef.child("users").observeEventType(.Value, withBlock: { snapshot in
let usersDict = snapshot.value as! [String:String]
self.usersArray= Array(usersDict.values)
self.tableView.reloadData()
}
})
}
*** Again Edit: Add this within the closure to account for when users has no data.
if let _ = snapshot.value as? NSNull {
return
} else {
let usersDict = snapshot.value as! [String:String]
self.usersArray= Array(usersDict.values)
self.tableView.reloadData()
}
Check my latest Questions, basically the same stuff. Checkout Firebase UI, very useful.
I used a ViewController with a table view and a Prototype Cell in it. Then this worked for me:
import Firebase
import FirebaseDatabaseUI
class LinksViewController: UIViewController, UITableViewDelegate {
var dataSource: FirebaseTableViewDataSource?
var ref = FIRDatabase.database().reference()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
dataSource = FirebaseTableViewDataSource.init(query: myQuery(),prototypeReuseIdentifier: "<Your-Identifier>", view: self.tableView)
dataSource?.populateCellWithBlock { (cell: UITableViewCell, obj: NSObject) -> Void in
//Populate your Cell here
}
tableView.dataSource = dataSource
tableView.delegate = self
}
}
Note that the myQuery is basically then your first line.