I am making a chat room app. Right now every chat post can only be seen by the user who posted it. How do i make the table view permanent and for all users to see not just the current user. Like a live feed.
Exaple of what i need to show on all devices not just the test device:
import UIKit
import Foundation
import Firebase
import FirebaseDatabase
import FirebaseStorage
struct postStruct {
let username : String!
let message : String!
let photoURL : String!
}
class GeneralChatroom: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {
#IBOutlet weak var messageTextField: UITextField!
var generalRoomDataArr = [postStruct]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140
let ref = FIRDatabase.database().reference()
let userID = FIRAuth.auth()?.currentUser?.uid
ref.child("general_room").child("chat").child(userID!).queryOrderedByKey().observe(.childAdded, with: {snapshot in
let snapDict = snapshot.value as? NSDictionary
let username = snapDict?["Username"] as? String ?? ""
let message = snapDict?["Message"] as? String ?? ""
let firebaseUserPhotoURL = snapDict?["photo_url"] as? String ?? ""
self.generalRoomDataArr.insert(postStruct(username: username, message: message, photoURL: firebaseUserPhotoURL), at: 0)
self.tableView.reloadData()
})
}
#IBAction func backButtonPressed(_ sender: UIButton) {
self.performSegue(withIdentifier: "BackToRoom", sender: nil)
}
//Message Send button is pressed data uploaded to firebase
#IBAction func sendButtonPressed(_ sender: UIButton) {
let message : String = self.messageTextField.text!
UploadGeneralChatRoom(message: message) //upload to general_room
self.messageTextField.text = nil
messageTextField.resignFirstResponder()//Quit keyboard
self.tableView.reloadData() //Reload tableView
//UploadUserData() //Update Rank in database
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return generalRoomDataArr.count // your number of cell here
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let usernameLabel = cell?.viewWithTag(1) as! UILabel
usernameLabel.text = generalRoomDataArr[indexPath.row].username
let messageLabel = cell?.viewWithTag(2) as! UILabel
messageLabel.numberOfLines=0 // line wrap
messageLabel.lineBreakMode = NSLineBreakMode.byWordWrapping
messageLabel.text = generalRoomDataArr[indexPath.row].message
//initialize UI Profile Image
let imageView = cell?.viewWithTag(3) as! UIImageView
//Make Porfile Image Cirlce
imageView.layer.cornerRadius = imageView.frame.size.width/2
imageView.clipsToBounds = true
//User Profile image in tableview
if generalRoomDataArr[indexPath.row].photoURL != nil
{
//let imageView = cell?.viewWithTag(3) as! UIImageView
if let url = NSURL(string: generalRoomDataArr[indexPath.row].photoURL) {
if let data = NSData(contentsOf: url as URL) {
imageView.image = UIImage(data: data as Data)
}
}
}
// your cell coding
return cell!
}
}//END CLASS
Try this code - Obj-C
[[[self.reference child:#"general_room"] child:#"chat"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
if (snapshot.value != (id)[NSNull null]) {
NSArray *value = [snapshot.value allValues];
NSLog(#"%#", [value valueForKey:#"Username"]);
NSLog(#"%#", [value valueForKey:#"Message"]);
NSLog(#"%#", [value valueForKey:#"photo_url"]);
}
} withCancelBlock:^(NSError * _Nonnull error) {
NSLog(#"%#", error.localizedDescription);
}];
In swift
Check with syntax am not sure about swift syntax
ref.child("general_room").child("chat").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSArray
for (dict in value) {
let username = dict?["Username"] as? String
let message = dict?["Message"] as? String
let photo_url = dict?["photo_url"] as? String
print(username)
print(message)
print(photo_url)
}
}) { (error) in
print(error.localizedDescription)
}}
Related
I am pushing data which is an array of strings to a tableview controller. These strings are "uid's" which are users in my database. With this array I make a call to firebase to extract all users and then do a match to the uid's. I am getting the correct data, yet I print out everything to make sure when the data is available and the data is available only after the tableview cell loads which causes the data to be nil causing a crash or just empty data. How can I make the data load first and then the cell so the data is available for display?
I've created functions for the data and now I have it in my viewDidLoad. Also, you'll see I have tried adding the firebase call into the Cell setup but of course that does not work.
Array of strings
var data = [String]()
viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
Database.database().reference().child("Businesses").observe(.value, with: { snapshot in
if snapshot.exists() {
self.businessUID = snapshot.value as? NSDictionary
if let dict = snapshot.value as? NSDictionary {
for item in dict {
let json = JSON(item.value)
let businessUid = json["uid"].stringValue
for uid in self.data {
if uid == businessUid {
let customerValue = self.businessUID?[uid]
self.businessDictionary = customerValue as! NSDictionary
print(self.businessDictionary)
print("Just printed the business dictionary")
}
}
}
}
} else {
print("does not exist")
}
})
}
Tableview Cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomerViewsSelectedBusinessesCell
print(self.businessDictionary)
print("Print the dictionary here to check the values")
let businessValues = self.businessDictionary
let uid = self.data.description
print(businessValues)
print("printed the business values")
if let dict = businessValues {
for item in dict {
let json = JSON(item.value)
let businessUid = json["uid"].stringValue
for uid in self.data {
if uid == businessUid {
let customerValue = self.businessUID?[uid]
self.businessData = customerValue as? NSDictionary
print(self.businessData)
print("Printing matching the uid values")
}
}
}
}
cell.businessName.text = businessData?["businessName"] as? String
cell.businessStreet.text = businessData?["businessStreet"] as? String
cell.businessCity.text = businessData?["businessCity"] as? String
cell.businessState.text = businessData?["businessState"] as? String
let businessProfilePicture = businessData?["profPicString"] as? String
if (businessProfilePicture!.characters.count) > 0 {
let url = URL(string: (businessProfilePicture!))
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
let image = UIImage(data: data!)?.potter_circle
cell.profileImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.profileImage.image = image
}
}
} else {
let image = UIImage(named: "default")?.potter_circle
cell.profileImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.profileImage.image = image
}
return cell
}
Here is my solution. Got it to work. Appened and used "usersArray" to get and display the data.
var data = [String]()
var usersArray = [NSDictionary?]()
override func viewDidLoad() {
super.viewDidLoad()
Database.database().reference().child("Businesses").observe(.value, with: { snapshot in
if snapshot.exists() {
self.businessUID = snapshot.value as? NSDictionary
if let dict = snapshot.value as? NSDictionary {
for item in dict {
let json = JSON(item.value)
let businessUid = json["uid"].stringValue
for uid in self.data {
if uid == businessUid {
let customerValue = self.businessUID?[uid]
self.usersArray.append(customerValue as! NSDictionary)
self.followUsersTableView.reloadData()
}
}
}
}
} else {
print("does not exist")
}
})
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.usersArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomerViewsSelectedBusinessesCell
let user : NSDictionary?
user = self.usersArray[indexPath.row]
cell.businessName.text = String(user?["businessName"] as! String)
cell.businessStreet.text = String(user?["businessStreet"] as! String)
cell.businessCity.text = String(user?["businessCity"] as! String)
cell.businessState.text = String(user?["businessState"] as! String)
let businessProfilePicture = String(user?["profPicString"] as! String)
if (businessProfilePicture.characters.count) > 0 {
let url = URL(string: (businessProfilePicture))
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
let image = UIImage(data: data!)?.potter_circle
cell.profileImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.profileImage.image = image
}
}
} else {
let image = UIImage(named: "default")?.potter_circle
cell.profileImage.contentMode = UIView.ContentMode.scaleAspectFill
cell.profileImage.image = image
}
return cell
}
I am trying to program a social media app and I want the posts to appear in the cells. The only things in the cell are a label for the post and a label for the topic. When I run the code there are no errors but the cells are not showing in the table. I cannot seem to figure out the problem. Thank you for your guys help.
import UIKit
import Firebase
import FirebaseDatabase
import FirebaseStorage
import FirebaseAuth
import SwiftKeychainWrapper
class FeedVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var postBtn: UIButton!
#IBOutlet var tableView: UITableView!
var posts = [Post]()
var post: Post!
var userName: String!
var refPosts: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
//getting a reference to the node posts
refPosts = Database.database().reference().child("posts")
//observing the data changes
refPosts.observe(DataEventType.value, with: { (snapshot) in
//if the reference have some values
if snapshot.childrenCount > 0 {
//clearing the list
self.posts.removeAll()
//iterating through all the values
for data in snapshot.children.allObjects as! [DataSnapshot] {
//getting values
let postObject = data.value as? [String: AnyObject]
let id = postObject?["id"]
let thought = postObject?["thought"]
let likes = postObject?["likes"]
//creating artist object with model and fetched values
let post = Post(_id: id as! String, _thought: thought as! String?, _likes: likes as! Int?)
//appending it to list
self.posts.append(post)
}
//reloading the tableview
self.tableView.reloadData()
}
})
/*refPosts.observe(.value, with: {(snapshot) in
if let snapshot = snapshot.children.allObjects as? [DataSnapshot] { //all objects in snapshot
self.posts.removeAll()
for data in snapshot {
print(data)
if let postDict = data.value as? Dictionary<String, AnyObject>{
let key = data.key
let post = Post(postKey: key, postData: postDict)
self.posts.append(post)
}
}
}
self.tableView.reloadData()
})*/
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell") as? PostCell{
cell.configCell(post: post)
return cell
}else{
return PostCell()
}
}
func PostToFirebase(imgUrl: String){
let userID = Auth.auth().currentUser?.uid
Database.database().reference().child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
if let data = snapshot.value as? Dictionary<String, AnyObject>{
//if let snap = snapshot.value as? [String:Any] {
let data = snapshot.value as! Dictionary<String, AnyObject>
let username = data["username"]
let post: Dictionary<String, AnyObject> = [
"username": username as AnyObject,
"likes": 0 as AnyObject
]
let firebasePost = Database.database().reference().child("posts").childByAutoId()
firebasePost.setValue(post)
self.tableView.reloadData()
}
})
}
#IBAction func postImageTapped(_ sender: AnyObject){//leads to text field box -> What are you thinking?
//present(imagePicker, animated: true, completion: nil)
//performSegue(withIdentifier: "UploadPage", sender: nil)
}
#IBAction func SignOut (_ sender: AnyObject){
try! Auth.auth().signOut()
KeychainWrapper.standard.removeObject(forKey: "uid")
dismiss(animated: true, completion: nil)
}
}
As #vadian mentioned in the comments, forcing the cast would be fine in this scenario considering you have everything setup correctly. Try adding the print statements I have below so you can see in more detail what it happening with the cells.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell") as? PostCell {
print("dequeueing cell")
cell.configCell(post: post)
return cell
}else{
print("initialising cell")
return PostCell()
}
}
The solution involves using the newer dequeue method that takes an index path and forcing the cast.
let post = posts[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! PostCell
cell.configCell(post: post)
return cell
I want to take the title and put it as titleText in the DestVC which is a label. How do I put it in the segue function?
import UIKit
import Firebase
import FirebaseDatabase
import SDWebImage
struct postStruct {
let title : String!
let author : String!
let date : String!
let article : String!
let downloadURL : String!
}
class ZeroHomeViewController: UITableViewController {
var posts = [postStruct]()
var downloadURL : String = ""
override func viewDidLoad() {
super.viewDidLoad()
let ref = Database.database().reference().child("Posts")
ref.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot.childrenCount)
for rest in snapshot.children.allObjects as! [DataSnapshot] {
guard let value = rest.value as? Dictionary<String,Any> else { continue }
guard let title = value["Title"] as? String else { continue }
guard let downloadURL = value["Download URL"] as? String else { continue }
guard let author = value["Author"] as? String else { continue }
guard let date = value["Date"] as? String else { continue }
guard let article = value["Article"] as? String else { continue }
let post = postStruct(title: title, author: author, date: date, article: article, downloadURL: downloadURL)
self.posts.append(post)
}
self.posts = self.posts.reversed(); self.tableView.reloadData()
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].title
let imageView = cell?.viewWithTag(2) as! UIImageView
let post = self.posts[indexPath.row];
imageView.sd_setImage(with: URL(string: post.downloadURL), placeholderImage: UIImage(named: "placeholder"))
return cell!
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detail" {
if let indexPath = tableView.indexPathForSelectedRow {
let destVC = segue.destination as! ArticleViewController
destVC.titleText = value["Title"] as? String
}
}
}
}
You simply need to access the relevant postStruct from your posts array and then get the title. You already have the index path for the selected row; the .row property will be the index in your posts array for the struct you need.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detail" {
if let indexPath = tableView.indexPathForSelectedRow {
let destVC = segue.destination as! ArticleViewController
destVC.titleText = posts[indexPath.row].title
}
}
}
I'm trying to retrieve my data as URL and pass my another viewController.
Here is my retrieve data's code:
private func loadPlaces() {
let ref = FIRDatabase.database().reference()
ref.child("places").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
let images = snapshot.value as! [String : AnyObject]
// self.places.removeAll()
for (_, value) in images {
let userToShow = historicalPlaces()
if let img = value["imagePath"] as? String,
let name = value["name"] as? String,
let information = value["information"] as? String
{
userToShow.historyImage = img
userToShow.historyName = name
userToShow.information = information
self.places.append(userToShow)
}
}
self.tableView.reloadData()
})
// ref.removeAllObservers()
}
In this code I'm using extension which can read URL.
Extension code here.
extension UIImageView {
func downloadImage(from imgURL: String!) {
let url = URLRequest(url: URL(string: imgURL)!)
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}
task.resume()
}
}
and here I print my images and labels to viewController.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "historyTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? HistoryTableViewCell else {
fatalError("The dequeued cell is not an instance of historyTableViewCell.")
}
let place = places[indexPath.row]
cell.nameLabel.text = place.historyName
cell.photoImageView.downloadImage(from: place.historyImage!)
return cell
}
and this is the cell control code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch(segue.identifier ?? "") {
case "ShowDetail":
guard let historyDetail = segue.destination as? selectedPlaceViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedPlace = sender as? HistoryTableViewCell else {
fatalError("Unexpected sender: \(sender)")
}
guard let indexPath = tableView.indexPath(for: selectedPlace) else {
fatalError("The selected cell is not being displayed by the table")
}
let Place = places[indexPath.row]
historyDetail.selectedPlaces = Place
default:
fatalError("Unexpected Segue Identifier; \(segue.identifier)")
}
}
now whenever I click the cell which has view and label print it's images and labels to another viewController.
I try something like this:
import UIKit
class selectedPlaceViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var photoImageView: UIImageView!
var selectedPlaces: historicalPlaces?
override func viewDidLoad() {
super.viewDidLoad()
nameTextField.delegate = self
if let history = selectedPlaces {
navigationItem.title = history.historyName
nameTextField.text = history.historyName
// photoImageView.downloadImage(from: selectedPlaces [IndexPath.init(row: 0, section: 0)].historyImage!)
}
}
}
In this code I can retrieve labels from another viewController but I can't retrieve images. How can I retrieve Images from another viewCell URL to image.
I have searched around to find an answer for my issue, but I had no luck. I'm new in coding, especially with Swift 3.0.
I'm trying to parse a YouTube playlist dynamically in a tableview using Alamofire cocoa pod in my project. My project contains: a viewcontroller called "videosViewController" which holds the tableview, a class called "Video", which holds the items I'm parsing from youtube API, and another class called "VideoModel" holds the method to pare those items. When I run my project the console parse the items successfully, but then the project crashes at the line of code:
for video in (data["items"] as? NSDictionary)!
with "Could not cast value of type '__NSArrayI' (0x10d2ebd88) to 'NSDictionary' (0x10d2ec288)." error as shown below
Project crash
Console details
And here the snippet of code I used:
videosViewController:
import UIKit
class videosViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
var videos:[Video] = [Video]()
var selectedVideo: Video?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let model = VideoModel()
model.fetchVideos()
self.tableView.dataSource = self
self.tableView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return (self.view.frame.size.width / 320) * 180
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return videos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BasicCell")!
let videoTitle = videos[indexPath.row].videoTitle
let label = cell.viewWithTag(2) as! UILabel
label.text = videoTitle
let videoThumbnailUrlString = "https://i1.ytimg.com/vi/" + videos[indexPath.row].videoId + "/maxresdefault.jpg"
let videoThumbnailUrl = NSURL(string: videoThumbnailUrlString)
if videoThumbnailUrl != nil {
let request = URLRequest(url: videoThumbnailUrl! as URL)
let session = URLSession.shared
let task = session.dataTask(with: request,
completionHandler: { (data:Data?,
response:URLResponse?,
error:Error?) -> Void in
DispatchQueue.main.async {
let imageView = cell.viewWithTag(1) as! UIImageView
imageView.image = UIImage(data: data!)
}
})
task.resume()
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedVideo = self.videos[indexPath.row]
self.performSegue(withIdentifier: "goToDetail", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let detailView = segue.destination as! videoDetailViewController
detailView.selectedVideo = self.selectedVideo
}
}
The Video class:
import UIKit
class Video: NSObject {
var videoId:String = ""
var videoTitle:String = ""
var videoDescription:String = ""
var videoThumbnailURL = ""
}
And the VideoModel class:
import UIKit
import Alamofire
class VideoModel: NSObject {
let parameters: Parameters = ["part":"snippet","playlistId":"PLMRqhzcHGw1ZRUB86rmNqG15Sr5jV-2NU","key":"AIzaSyDdNXhz3H7ifXB-qfOVakz0Xps2Y-kP0R0"]
var videoArray = [Video]()
func fetchVideos() {
Alamofire.request("https://www.googleapis.com/youtube/v3/playlistItems", method: .get, parameters: parameters, encoding: URLEncoding.default, headers: nil).responseJSON { (response:DataResponse<Any>) in
switch(response.result) {
case .success(let JSON):
print("Success with JSON: \(JSON)")
if let data = response.result.value as? [String: AnyObject] {
// print(response.result.value)
var arrayOfVideos = [Video]()
for video in (data["items"] as? NSDictionary)! {
let videoObj = Video()
videoObj.videoId = (video.value as? NSDictionary)?["snippet.resourceId.videoId"] as? String ?? ""
videoObj.videoTitle = (video.value as? NSDictionary)?["snippet.title"] as? String ?? ""
videoObj.videoDescription = (video.value as? NSDictionary)?["snippet.description"] as? String ?? ""
videoObj.videoThumbnailURL = (video.value as? NSDictionary)?["snippet.thumbnails.maxres.url"] as? String ?? ""
print(video)
// You need to parse the items into the video data
arrayOfVideos.append(videoObj)
}
self.videoArray = arrayOfVideos
// }
}
case .failure(let error):
print("Request failed with error: \(error)")
}
}
}
Replace
as? NSDictionary
with
as? [String:Any]
in
for video in (data["items"] as? NSDictionary)!
Bcs: You have to cast type Any to Swift dictionary type [String:Any].
if let JSON = response.result.value as? [String : Any] {
if let items = JSON["items"] as? [[String : Any]] {
for video in items {
//Other code
}
}
}