I am getting error in ToDoViewController.Swift here is the overall code:
I am makeing a simple app for taking orders from customers. and orders will store in Firebase.
View Controller.Swift
import UIKit
import Firebase
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var todoList = [Todo]()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
loadData()
}
func loadData() {
self.todoList.removeAll()
let ref = FIRDatabase.database().reference()
ref.child("todoList").observeSingleEvent(of: .value, with: { (snapshot) in
if let todoDict = snapshot.value as? [String:AnyObject] {
for (_,todoElement) in todoDict {
print(todoElement);
let todo = Todo()
todo.name = todoElement["name"] as? String
todo.message = todoElement["message"] as? String
todo.reminderDate = todoElement["date"] as? String
self.todoList.append(todo)
}
}
self.tableView.reloadData()
}) { (error) in
print(error.localizedDescription)
}
}
//MARK: TableView datasource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.todoList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ToDoCell")
cell!.textLabel?.text = todoList[indexPath.row].name
return cell!
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
ToDoViewController
import UIKit
import Firebase
class ToDoViewController: UIViewController {
var todo:Todo?
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var messageField: UITextField!
#IBOutlet weak var dateFormatter: UIDatePicker!
#IBAction func Done(_ sender: Any) {
if todo == nil {
todo = Todo()
}
// first section
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy hh:mm"
todo?.name = self.nameField.text
todo?.message = self.messageField.text
todo?.reminderDate = dateFormatter.string(from: self.dateFormatter.date)
//second section
let ref = FIRDatabase.database().reference()
let key = ref.child("todoList").childByAutoId().key
let dictionaryTodo = [ "name" : todo!.name! ,
"message" : todo!.message!,
"date" : todo!.reminderDate!]
let childUpdates = ["/todoList/\(key)": dictionaryTodo]
ref.updateChildValues(childUpdates, withCompletionBlock: { (error, ref) -> Void in
self.navigationController?.popViewController(animated: true)
})
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let todoVC = self.storyboard!.instantiateViewController(withIdentifier: "ToDoVC") as! ToDoViewController
todoVC.todo = todoList[indexPath.row]
self.navigationController?.pushViewController(todoVC, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if self.todo != nil {
nameField.text = self.todo?.name
messageField.text = self.todo?.message
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy hh:mm"
let date = dateFormatter.date(from: self.todo!.reminderDate!)
datePicker.date = date!
}
}
}
Todo.Swift
import UIKit
class Todo: NSObject {
var name :String?
var message: String?
var reminderDate: String?
// id which is set from firebase to uniquely identify it
var uniqueId:String?
}
Something is wrong with your code:
Use of un-resolved identifier 'todoList':
todoList is declared in View Controller.Swift and you cannot use it in ToDoViewController.
Cannot assign to property: 'date' is a method:
In the code datePicker is undefined and #IBOutlet weak var dateFormatter: UIDatePicker! should be:
#IBOutlet weak var datePicker: UIDatePicker!
AND In the image dateFormatter.date = date! should be:
datePicker.date = date!
Related
I need help with solving one problem with core data. I have a news app with two view controllers. In the main view controller I'm loading news data in table view in custom cell. Here I have a button, on which should I tap and save news to another view controller. How it looks now: How it looks So when we tap on this blue button, it should save news from the cell and display on second view controller. I have created a core data model like this: Core data model Here is code in my first view controller:
import UIKit
import SafariServices
import CoreData
class ViewController: UIViewController, UISearchBarDelegate, UpdateTableViewDelegate {
#IBOutlet weak var pecodeTableView: UITableView!
private var articles = [News]()
// private var viewModels = [NewsTableViewCellViewModel]()
private var viewModel = NewsListViewModel()
var newsTitle: String?
var newsAuthor: String?
var newsDesc: String?
var urlString: String?
var newsDate: String?
private let searchVC = UISearchController(searchResultsController: nil)
var selectedRow = Int()
override func viewDidLoad() {
super.viewDidLoad()
pecodeTableView.delegate = self
pecodeTableView.dataSource = self
pecodeTableView.register(UINib(nibName: S.CustomCell.customNewsCell, bundle: nil), forCellReuseIdentifier: S.CustomCell.customCellIdentifier)
// fetchAllNews()
viewModel.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
categoryMenu()
loadData()
}
private func loadNewsData(api: String){
let apiService = APIService(categoryCode: api)
apiService.getNewsData {(result) in
switch result{
case .success(let NewsOf):
CoreData.sharedInstance.saveDataOf(news: NewsOf.articles)
case .failure(let error):
print("Error processing json data: \(error)")
}
}
}
func reloadData(sender: NewsListViewModel) {
self.pecodeTableView.reloadData()
}
//MARK: - Networking
private func loadData(){
viewModel.retrieveDataFromCoreData()
}
//MARK: - UIView UImenu
func categoryMenu(){
var categoryAction: UIMenu{
let menuAction = Category.allCases.map { (item) -> UIAction in
let name = item.rawValue
return UIAction(title: name.capitalized, image: UIImage(systemName: item.systemImage)) { [weak self](_) in
self?.loadNewsData(api: name)
self?.loadData()
self?.reloadData(sender: self!.viewModel)
}
}
return UIMenu(title: "Change Category", children: menuAction)
}
let categoryButton = UIBarButtonItem(image: UIImage(systemName: "scroll"), menu: categoryAction)
navigationItem.leftBarButtonItem = categoryButton
}
#IBAction func goToFavouritesNews(_ sender: UIButton) {
performSegue(withIdentifier: S.Segues.goToFav, sender: self)
}
private func createSearchBar() {
navigationItem.searchController = searchVC
searchVC.searchBar.delegate = self
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.numberOfRowsInSection(section: section)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: S.CustomCell.customCellIdentifier, for: indexPath) as? CustomNewsCell
let object = viewModel.object(indexPath: indexPath)!
cell?.setCellWithValuesOf(object)
cell?.saveNewsBtn.tag = indexPath.row
cell?.saveNewsBtn.addTarget(self, action: #selector(didTapCellButton(sender:)), for: .touchUpInside)
return cell!
}
#objc func didTapCellButton(sender: FavouritesCell) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "SavedNews", in: context)
let newNewsSave = SavedNews(entity: entity!, insertInto: context)
newNewsSave.author = newsAuthor
newNewsSave.desc = newsDesc
newNewsSave.title = newsTitle
do {
try context.save()
savedNews.append(newNewsSave)
navigationController?.popViewController(animated: true)
} catch {
print("Error saving")
}
print("Done")
}
Also I want to show you my parsing functions and model for this: *APIService.swift*
import Foundation
class APIService{
private var dataTask: URLSessionDataTask?
public let resourceURL: URL
private let API_KEY = "e2a69f7f9567451ba484c85614356c30"
private let host = "https://newsapi.org"
private let headlines = "/v2/top-headlines?"
init(categoryCode: String){
let resourceString = "\(host)\(headlines)country=us&category=\(categoryCode)&apiKey=\(API_KEY)"
print(resourceString)
guard let resourceURL = URL(string: resourceString) else {
fatalError()
}
self.resourceURL = resourceURL
}
//MARK: - Get News
func getNewsData(completion: #escaping (Result<Articles, Error>) -> Void){
dataTask = URLSession.shared.dataTask(with: resourceURL) { (data, response, error) in
if let error = error{
completion(.failure(error))
print("DataTask error: - \(error.localizedDescription)")
}
guard let response = response as? HTTPURLResponse else{
print("Empty Response")
return
}
print("Response status code: - \(response.statusCode)")
guard let data = data else {
print("Empty Data")
return
}
do{
let decoder = JSONDecoder()
let jsonData = try decoder.decode(Articles.self, from: data)
DispatchQueue.main.async {
completion(.success(jsonData))
}
}catch let error{
completion(.failure(error))
}
}
dataTask?.resume()
}
}
And here is NewsModel.swift:
import Foundation
struct Articles: Codable {
let articles: [News]
private enum CodingKeys: String, CodingKey{
case articles = "articles"
}
}
struct News: Codable {
let author: String?
let source: Source
let title: String
let description: String?
let url: URL?
let urlToImage: URL?
let publishedAt: String?
private enum CodingKeys: String, CodingKey{
case author = "author"
case title = "title"
case url = "url"
case urlToImage = "urlToImage"
case publishedAt = "publishedAt"
case description = "description"
case source = "source"
}
}
struct Source: Codable {
let name: String?
}
Here is my code in CustomNewsCell.swift:
import UIKit
protocol CustomNewsDelegate: AnyObject {
func btnFavPress(cell: CustomNewsCell)
}
private var loadImage = LoadToImage()
private var formatDate = FormatDate()
class CustomNewsCell: UITableViewCell {
weak var delegate: CustomNewsDelegate?
#IBOutlet weak var saveNewsBtn: UIButton!
#IBOutlet weak var imageOutlet: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descLabel: UILabel!
#IBOutlet weak var authorLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setCellWithValuesOf(_ news: SavedNews){
updateUI(title: news.title, url: news.url, urlToImage: news.urlToImage, publishedAt: news.publishedAt, author: news.author ?? "No author", description: news.desc, source: news.source)
}
private func updateUI(title: String?, url: URL?, urlToImage: URL?, publishedAt: String?, author: String, description: String?, source: String?){
//title
self.titleLabel.text = title
self.authorLabel.text = author
self.descLabel.text = description
//date
let dateString = formatDate.formatDate(from: publishedAt ?? "")
let date = formatDate.formatDateString(from: dateString)
self.dateLabel.text = date
//image
guard let urlToImageString = urlToImage else {return}
imageOutlet.image = nil
loadImage.getImageDataFrom(url: urlToImageString) { [weak self] data in
guard let data = data, let image = UIImage(data: data) else{
DispatchQueue.main.async {
self?.imageOutlet.image = UIImage(named: "noImage")
}
return
}
self?.imageOutlet.image = image
}
}
#IBAction func saveBtnPressed(_ sender: UIButton) {
delegate?.btnFavPress(cell: self)
}
}
First time I've tried with delegate method for this blue button, but now as you can see I've created a selector method. Maybe it's not correct and need to fix it. Here is the code in the second view controller, which should show saved news from first view controller:
import UIKit
import CoreData
var savedNews = [SavedNews]()
class FavouriteNewsViewController: UIViewController {
#IBOutlet weak var favTableView: UITableView!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var result: [SavedNews] = []
var newsTitleNew: String?
var newsDescNew: String?
var newsAuthor: String?
override func viewDidLoad() {
super.viewDidLoad()
favTableView.delegate = self
favTableView.delegate = self
fetch()
// loadSavedNews()
favTableView.register(UINib(nibName: S.FavouriteCell.favouriteCell, bundle: nil), forCellReuseIdentifier: S.FavouriteCell.favouriteCellIdentifier)
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
// fetch()
favTableView.reloadData()
}
#IBAction func goToNewsFeed(_ sender: UIButton) {
performSegue(withIdentifier: S.Segues.goToNewsFeed, sender: self)
}
}
extension FavouriteNewsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return savedNews.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 140
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = favTableView.dequeueReusableCell(withIdentifier: S.FavouriteCell.favouriteCellIdentifier, for: indexPath) as! FavouritesCell
let newRslt: SavedNews!
newRslt = savedNews[indexPath.row]
cell.favAuthor.text = newRslt.author
cell.favDesc.text = newRslt.desc
cell.favTitle.text = newRslt.title
return cell
}
func fetch() {
let request = NSFetchRequest<SavedNews>(entityName: "SavedNews")
do {
savedNews = try context.fetch(request)
} catch {
print(error)
}
}
}
And code for cell for this controller:
import UIKit
import CoreData
class FavouritesCell: UITableViewCell {
#IBOutlet weak var favImage: UIImageView!
#IBOutlet weak var favTitle: UILabel!
#IBOutlet weak var favDesc: UILabel!
#IBOutlet weak var favAuthor: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
If somebody can help with this, it will be amazing. Because I really don't how to do this. Thank you!
What I got so far is a tableView and custom Cells about hookah tobacco. Those include an image, name, brand and ID. Now what I try to reach is basically a tableview that contains only the cells with attributes based on a "filter". For example the tableView that appears at the beginning has only the following two settings to make it simple: PriceRange and BrandName. At the first time loading the tableView those are PriceRange: 0 - 100 and Brands: all brands. Then imagine a user restricting those like 0 - 15 Euros and only brand called "7 Days". How exactly would I do that with reloading the tableView?
import UIKit
import Firebase
class ShopViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var button_filter: UIBarButtonItem!
#IBOutlet weak var searchBar_shop: UISearchBar!
#IBOutlet weak var view_navigator: UIView!
#IBOutlet weak var tableView_shop: UITableView!
var ShopCells: [ShopCell] = []
var databaseRef: DatabaseReference!
var storageRef: StorageReference!
override func viewDidLoad() {
super.viewDidLoad()
self.databaseRef = Database.database().reference()
self.storageRef = Storage.storage().reference()
createArray() { shopCells in
for item in shopCells {
self.ShopCells.append(item)
}
DispatchQueue.main.async {
self.tableView_shop.reloadData()
}
}
self.navigationItem.title = "Shop"
self.tableView_shop.delegate = self
self.tableView_shop.dataSource = self
self.searchBar_shop.delegate = self
self.searchBar_shop.barTintColor = UIColor(hexString: "#1ABC9C")
self.view_navigator.backgroundColor = UIColor(hexString: "#1ABC9C")
self.tableView_shop.separatorColor = UIColor.clear
self.searchBar_shop.isTranslucent = false
self.searchBar_shop.backgroundImage = UIImage()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ShopViewController.viewTapped(gestureRecognizer:)))
view.addGestureRecognizer(tapGesture)
}
#objc func viewTapped(gestureRecognizer: UITapGestureRecognizer) {
view.endEditing(true)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.searchBar_shop.resignFirstResponder()
}
func createArray(completion: #escaping ([ShopCell]) -> () ) {
var tempShopCells: [ShopCell] = []
let rootRef = Database.database().reference()
let query = rootRef.child("tobaccos").queryOrdered(byChild: "name")
query.observeSingleEvent(of: .value) { (snapshot) in
let dispatchGroup = DispatchGroup()
for child in snapshot.children.allObjects as! [DataSnapshot] {
let value = child.value as? [String: Any];
let name = value?["name"] as? String ?? "";
let brand = value?["brand"] as? String ?? "";
let iD = value?["iD"] as? String ?? "";
dispatchGroup.enter()
let imageReference = Storage.storage().reference().child("tobaccoPictures").child("\(iD).jpg")
imageReference.getData(maxSize: (1 * 1024 * 1024)) { (data, error) in
if let _error = error{
print(_error)
} else {
if let _data = data {
let image: UIImage! = UIImage(data: _data)
tempShopCells.append(ShopCell(productName: name, brandName: brand, productImage: image, iD: iD))
}
}
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
completion(tempShopCells)
}
}
}
}
extension ShopViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.ShopCells.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let shopCell = ShopCells[indexPath.row]
let cell = tableView_shop.dequeueReusableCell(withIdentifier: "ShopCell") as! ShopTableViewCell
cell.setShopCell(shopCell: shopCell)
return cell
}
}
This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 4 years ago.
So I've gone through every possible solution offered on SO and tried all the solutions but my case seems to be singular. I have a tableview in a 'HomeViewController' which is connected to the HomeViewController.swift file and there's also a HomeViewControllerTableViewCell.swift file. Here's my code:
HomeViewController.swift:
import Firebase
import FirebaseDatabase
import UIKit
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return artistlists.count
}
var artistlists = [ArtistList]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "celler", for: indexPath) as! HomeViewControllerTableViewCell
let artists : ArtistList
artists=artistlists[indexPath.row]
print("Here")
cell.labelName.text = artists.name
cell.labelCity.text = artists.city
cell.labelDate.text = artists.date
return cell
}
#IBOutlet weak var tableArtists: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
var ref: DatabaseReference!
ref = Database.database().reference()
ref.child("Fund").observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot.childrenCount)
if snapshot.childrenCount > 0 {
self.artistlists.removeAll()
for artist in snapshot.children.allObjects as! [DataSnapshot] {
//temporary test code
if let artistDict = artist.value as? [String: Any] {
if let aName = artistDict["artist_name"] as? String {
print("first name was \(aName)")
} else {
print("ARTIST NAME WAS NIL! RUN FOR YOUR LIVES!")
}
} else {
print("artist.value was nil!")
}
//end of temporary test code
// let artistObject = artist.value as? [String: AnyObject]
// let artistName = artistObject?["artist_name"]
// let artistCity = artistObject?["perf_location"]
// let artistDate = artistObject?["target_date"]
// let artistName2:String = String(format: "%#", artistName as! CVarArg)
// print(artistName2)
// let artistCity2:String = String(format: "%#", artistCity as! CVarArg)
// let artistDate2:String = String(format: "%#", artistDate as! CVarArg)
// let artisty = ArtistList(name: artistName2 as String?, city: artistCity2 as String?, date: artistDate2 as String?)
// self.artistlists.append(artisty)
}
self.tableArtists.reloadData()
}
// Do any additional setup after loading the view.
})
{ (error) in
print(error.localizedDescription)
}
self.tableArtists.register(HomeViewControllerTableViewCell.self, forCellReuseIdentifier: "celler")
}
HomeViewControllerTableViewCell.swift
import UIKit
import FirebaseDatabase
import Firebase
class HomeViewControllerTableViewCell: UITableViewCell {
#IBOutlet weak var labelName: UILabel!
#IBOutlet weak var labelCity: UILabel!
#IBOutlet weak var labelDate: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
ArtistList.swift:
import Foundation
class ArtistList
{
var name: String?
var city: String?
var date: String?
init(name: String?, city: String?, date: String?) {
self.city = city
self.date = date
self.name = name
}
}
I'm getting an error in HomeViewController.swift, line 28-30, saying 'Unexpectedly found nil while wrapping an optional value'.
cell.labelName.text = artists.name,
cell.labelCity.text = artists.city,
cell.labelDate.text = artists.date
are lines 28-30.
Can someone please help me out with this?
Broken outlet links are a very common cause of such errors. My guess is either the outlet links to labelName, labelCity, and labelDate are not connected in your HomeViewControllerTableViewCell, or your cell with the identifier "celler" is not set up as a as a cell of type HomeViewControllerTableViewCell.
I'm using firebase to make an iOS app. I want to retrieve all the users on my database and display their name and profile picture in a table view. Here is my code for my TableViewCell:
import UIKit
import FirebaseDatabase
import FirebaseAuth
import SDWebImage
class HomeTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var likeImageView: UIImageView!
#IBOutlet weak var messageImageView: UIImageView!
#IBOutlet weak var likeCountButton: UIButton!
var homeVC: HomeViewController?
var postReference: DatabaseReference!
var post: UserFile?{
didSet {
updateView()
}
}
var user: UserFile? {
didSet {
updateUserInfo()
}
}
override func awakeFromNib() {
super.awakeFromNib()
nameLabel.text = ""
let berryTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleLikeTap))
likeImageView.addGestureRecognizer(berryTapGesture)
likeImageView.isUserInteractionEnabled = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func updateView() {
if let photoURL = post?.picURL {
profileImageView.sd_setImage(with: URL(string: photoURL))
}
API.Post.REF_POSTS.child(post!.id!).observeSingleEvent(of: .value, with: { postSnapshot in
if let postDictionary = postSnapshot.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: postSnapshot.key)
self.updateLike(post: post)
}
})
API.Post.REF_POSTS.child(post!.id!).observe(.childChanged, with: { snapshot in
if let value = snapshot.value as? Int {
self.likeCountButton.setTitle("\(value) berries", for: .normal)
}
})
}
func updateLike(post: UserFile) {
let imageName = post.berries == nil || !post.isBerried! ? "berry" : "berrySelected"
likeImageView.image = UIImage(named: imageName)
// display a message for berries
guard let count = post.berryCount else {
return
}
if count != 0 {
likeCountButton.setTitle("\(count) berries", for: .normal)
} else if post.berryCount == 0 {
likeCountButton.setTitle("Be the first to Like this", for: .normal)
}
}
func incrementberries(forReference ref: DatabaseReference) {
ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = Auth.auth().currentUser?.uid {
var berries: Dictionary<String, Bool>
berries = post["berries"] as? [String : Bool] ?? [:]
var likeCount = post["berryCount"] as? Int ?? 0
if let _ = berries[uid] {
// Unlike the post and remove self from stars
likeCount -= 1
berries.removeValue(forKey: uid)
} else {
// Like the post and add self to stars
likeCount += 1
berries[uid] = true
}
post["berryCount"] = likeCount as AnyObject?
post["berries"] = berries as AnyObject?
currentData.value = post
return TransactionResult.success(withValue: currentData)
}
return TransactionResult.success(withValue: currentData)
}) { (error, committed, snapshot) in
if let error = error {
print(error.localizedDescription)
}
if let postDictionary = snapshot?.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: snapshot!.key)
self.updateLike(post: post)
}
}
}
func handleLikeTap() {
postReference = API.Post.REF_POSTS.child(post!.id!)
incrementberries(forReference: postReference)
}
override func prepareForReuse() {
super.prepareForReuse()
profileImageView.image = UIImage(named: "industribune-default-no-profile-pic")
}
func updateUserInfo() {
nameLabel.text = user?.username
if let photoURL = user?.profileImageURL {
profileImageView.sd_setImage(with: URL(string: photoURL), placeholderImage: UIImage(named: "industribune-default-no-profile-pic"))
}
}
}
I am displaying this cell on my HomeViewController:
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import Firebase
class HomeViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
var posts = [UserFile]()
var users = [UserFile]()
override func viewDidLoad() {
super.viewDidLoad()
// for performance set an estimated row height
tableView.estimatedRowHeight = 1
// but also request to dynamically adjust to content using AutoLayout
tableView.rowHeight = UITableViewAutomaticDimension
//tableView.delegate = self
tableView.dataSource = self
loadPosts()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadPosts() {
activityIndicatorView.startAnimating()
API.User.observePosts { (newPost) in
guard let userID = newPost.uid else { return }
self.fetchUser(uid: userID, completed: {
// append the new Post and Reload after the user
// has been cached
self.posts.append(newPost)
self.activityIndicatorView.stopAnimating()
self.tableView.reloadData()
})
}
}
func fetchUser(uid: String, completed: #escaping () -> Void) {
API.User.observeUser(withID: uid) { user in
self.users.append(user)
completed()
}
}
}
extension HomeViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
cell.post = posts[indexPath.row]
cell.user = users[indexPath.row]
cell.homeVC = self
return cell
}
}
I have a lot of craziness going on in my project so let me know if you have any questions and what I'm doing wrong. If it's too complicated to understand I'm ready to erase everything and start over too.
And I do honestly think that I followed all the guidelines to ask a question so don't like shut this question down or something.
That's a lot of code. Try this super reduced example. For this, the users node only stores the name as a child node but it could also have an image, email, address, etc.
Example users node
users
uid_0:
name: "Bert"
uid_1:
name: "Ernie"
and some code
var usersArray = [ [String: Any] ]() //an array of dictionaries.
class ViewController: UIViewController {
//set up firebase references here
override func viewDidLoad() {
super.viewDidLoad()
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let userDict = snap.value as! [String: Any]
self.usersArray.append(userDict)
}
self.tableView.reloadData()
})
and the tableView delegate methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.usersArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
let userDict = self.usersArray[indexPath.row]
cell.text = userDict["name"] as! String
//cell.imge = userDict["image"] etc etc
return cell
}
Now... that all being said. This is the perfect use for an array of UserClass objects instead of the dictionaries.
Here's a starting point....
class UserClass {
var name = ""
var image = ""
func init(snap: DataSnapshot) {
//populate the vars from the snapshot
}
}
var userClassArray = [UserClass]()
Don't copy and paste this as there are probably typos but it should point you in the right direction.
I am creating an inventory app in order to keep track of items held in a laboratory. In the laboratory there are different stations which contain different items in them, which as you can see is structured properly in my Firebase database.
Firebase Database
Iphone Simulator
My problem comes when I try to delete a particular item out of the tableCell. I am able to remove it from the UI but in firebase the data still remains. I have done coutless reserch but am not able to find anything relating to this particular problem.
Data Services Class
let DB_BASE = FIRDatabase.database().reference().child("laboratory") //contains the root of our database
let STORAGE_BASE = FIRStorage.storage().reference()
class DataService {
static let ds = DataService()
//DB References
private var _REF_BASE = DB_BASE
private var _REF_STATION = DB_BASE.child("stations")
private var _REF_USERS = DB_BASE.child("users")
//Storage Reference
private var _REF_ITEM_IMAGE = STORAGE_BASE.child("item-pics")
var REF_BASE: FIRDatabaseReference {
return _REF_BASE
}
var REF_STATION: FIRDatabaseReference {
return _REF_STATION
}
var REF_USERS: FIRDatabaseReference {
return _REF_USERS
}
var REF_ITEM_IMAGES: FIRStorageReference {
return _REF_ITEM_IMAGE
}
//creating a new user into the firebase database
func createFirebaseDBUser(_ uid: String, userData: Dictionary<String, String>) {
REF_USERS.child(uid).updateChildValues(userData)
}
}
Inventory View Controller
import UIKit
import Firebase
class InventoryViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var items = [Item]()
private var _station: Station!
private var _item: Item!
var sortIndex = 3
var imagePicker: UIImagePickerController!
static var imageCache: NSCache<NSString, UIImage> = NSCache()
var imageSelected = false
#IBOutlet weak var itemImageToAdd: UIImageView!
#IBOutlet weak var objectTextInput: UITextField!
#IBOutlet weak var brandTextInput: UITextField!
#IBOutlet weak var unitTextInput: UITextField!
#IBOutlet weak var amountTextInput: UITextField!
#IBOutlet weak var tableView: UITableView!
#IBOutlet var addItemView: UIView!
#IBOutlet weak var currentStationLabel: UILabel!
var station: Station {
get {
return _station
} set {
_station = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
var currentStationName = station.title
currentStationLabel.text = currentStationName
self.items = []
let currentStation = station.title
let stationRef = DataService.ds.REF_STATION.child(currentStation!)
let inventoryRef = stationRef.child("inventory")
tableView.delegate = self
tableView.dataSource = self
imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
inventoryRef.observe(.value, with: { (snapshot) in
print(snapshot.value!)
self.items = []
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
print("SNAP: \(snap)")
if let itemDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let item = Item(itemKey: key,
itemData: itemDict)
self.items.append(item)
}
}
}
self.tableView.reloadData()
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "inventoryTableCell", for: indexPath) as? ItemCell {
if let img = InventoryViewController.imageCache.object(forKey: NSString(string: item.imageURL!)) {
cell.updateItemUI(item: item, img: img)
} else {
cell.updateItemUI(item: item)
}
return cell
} else {
return ItemCell()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func postToFirebase(itemImageURL: String) {
let post: Dictionary<String, AnyObject> = [
"objectLabel": objectTextInput.text! as AnyObject,
"brandLabel": brandTextInput.text! as AnyObject,
"unitLabel": unitTextInput.text! as AnyObject,
"amountLabel": amountTextInput.text! as AnyObject,
//post elsewhere as an image for future reference
"itemImageURL": itemImageURL as AnyObject,
]
let stationText = _station.title
let stationRef = DataService.ds.REF_STATION.child(stationText!)
let inventoryRef = stationRef.child("inventory")
let firebasePost = inventoryRef.childByAutoId()
firebasePost.setValue(post)
objectTextInput.text = ""
brandTextInput.text = ""
unitTextInput.text = ""
amountTextInput.text = ""
imageSelected = false
tableView.reloadData()
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
itemImageToAdd.image = image
imageSelected = true
} else {
print("Please select a valid image")
}
imagePicker.dismiss(animated: true, completion: nil)
}
#IBAction func backToStations(_ sender: Any) {
performSegue(withIdentifier: "backToStations", sender: nil)
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(tableView: (UITableView!), commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: (NSIndexPath!)) {
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let currentStation = station.title
let stationRef = DataService.ds.REF_STATION.child(currentStation!)
let inventoryRef = stationRef.child("inventory")
var deleteAction = UITableViewRowAction(style: .default, title: "Delete") {action in
//Insert code to delete values from Firebase
self.items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath as IndexPath], with: .fade)
}
var editAction = UITableViewRowAction(style: .normal, title: "Edit") { action in
}
return [deleteAction, editAction]
}
}
My thought process is upon delete to call self_items.key reffering to the current key of the particular tableCell row. From there I would use the current key whick would be the autoID and remove the value that way. Unfortunatly though that crashes the program with a fatal nil error.
The best way I've found to solve this problem is in your delete action, delete the object from Firebase. Do not alter the tableview from here.
Then in your data listener, check when the data comes back as deleted(or NULL), then remove it from the tableview datasource array and update the tableview.