Cannot fetch data from google-cloud-firestore correctly - swift

No problems with fetching Title and Subtitle. Problems are with downloading of images and streaming the video. I really appreciate your help friends. I just want to learn how to implement it correctly and what to use, get data method or listener? Does it need grammatical changes of my code or a pair of code lines?
import UIKit
import AVKit
import AVFoundation
import FirebaseFirestore
import Combine
class ListOfVideoLessonsTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let pinchGesture = UIPinchGestureRecognizer()
private var viewModel = VideosViewModel()
private var cancellable: AnyCancellable?
var player = AVPlayer()
var playerViewController = AVPlayerViewController()
#IBOutlet var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel.fetchData()
self.title = "Video Lessons"
table.delegate = self
table.dataSource = self
cancellable = viewModel.$videos.sink { _ in
DispatchQueue.main.async{
self.table.reloadData()
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("videos count = ", viewModel.videos.count)
return viewModel.videos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let video = viewModel.videos[indexPath.row]
tableView.tableFooterView = UIView()
//configure cell
cell.textLabel?.text = video.name
cell.detailTextLabel?.text = video.lessonName
cell.accessoryType = .disclosureIndicator
let image = UIImage(data: video.imageData)
cell.imageView?.image = image...
imageName!.withRenderingMode(.alwaysTemplate)
let backgroundView = UIView()
backgroundView.backgroundColor = UIColor(named: "VideoLessonsCellHighlighted")
cell.selectedBackgroundView = backgroundView
cell.textLabel?.font = UIFont(name: "Helvetica-Bold", size: 14)
cell.detailTextLabel?.font = UIFont(name: "Helvetica", size: 12)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
playVideo(at: indexPath)
}
func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.contentView.backgroundColor = UIColor(named: "VideoLessonsCellHighlighted")
cell.textLabel?.highlightedTextColor = UIColor(named: "textHighlighted")
cell.detailTextLabel?.highlightedTextColor = UIColor(named: "textHighlighted")
}
}
func tableView(_ tableView: UITableView, didUnhighlightRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.contentView.backgroundColor = nil
}
}
func playVideo(at indexPath: IndexPath){
let selectedVideo = viewModel.videos[indexPath.row]
let videoURL = URL(string: selectedVideo.fileURL)
player = AVPlayer(url: videoURL!)
playerViewController.player = player
self.present(playerViewController, animated: true, completion: {
self.playerViewController.player?.play()
})
}
}
This is the VideosViewModel which supposed to download data from firestore database and translate into local file names if I am not mistaken.
import Foundation
import FirebaseFirestore
class VideosViewModel: ObservableObject {
#Published var videos = [Video]()
private var db = Firestore.firestore()
static func downloadImage(withURL url: URL, completion: #escaping (_ _image:UIImage?)->()) {
let dataTask = URLSession.shared.dataTask(with: url) { data, url, error in
var downloadedImage:UIImage?
if let data = data {
downloadedImage = UIImage(data: data)
}
DispatchQueue.main.async {
completion(downloadedImage)
}
}
dataTask.resume()
}
func fetchData() {
db.collection("videos").addSnapshotListener { [self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No Documents")
return
}
self.videos = documents.map { (queryDocumentSnapshot) -> Video in
let data = queryDocumentSnapshot.data()
let name = data["name"] as? String ?? ""
let imageName = data["imageName"]as? URL ?? ""
let lessonName = data["lessonName"] as? String ?? ""
let fileURL = data["fileURL"] as? String ?? ""
print(data)
return Video(name: name, imageName: imageName, lessonName: lessonName, fileURL: fileURL)
}
}
}
}
import Foundation
struct Video {
var name: String
var imageName: Data
var lessonName: String
var fileURL: String
}```
[1]: https://i.stack.imgur.com/YIU5O.png

Related

Cannot download image from Cloud Firestore database

This is the tableviewController which supposed to show image in each row but it does not. When I run the application on simulator it starts and displays the table view with titles and subtitles but images. I also attach the screenshots of simulator with some prints and of the structure of the database.
import UIKit
import AVKit
import AVFoundation
import FirebaseFirestore
import Combine
class ListOfVideoLessonsTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let pinchGesture = UIPinchGestureRecognizer()
private var viewModel = VideosViewModel()
private var cancellable: AnyCancellable?
var player = AVPlayer()
var playerViewController = AVPlayerViewController()
#IBOutlet var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel.fetchData()
self.title = "Video Lessons"
table.delegate = self
table.dataSource = self
cancellable = viewModel.$videos.sink { _ in
DispatchQueue.main.async{
self.table.reloadData()
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("videos count = ", viewModel.videos.count)
return viewModel.videos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let video = viewModel.videos[indexPath.row]
tableView.tableFooterView = UIView()
cell.textLabel?.text = video.name
cell.detailTextLabel?.text = video.lessonName
cell.accessoryType = .disclosureIndicator
let image = UIImage(named: video.imageName)
cell.imageView?.image = image
let backgroundView = UIView()
backgroundView.backgroundColor = UIColor(named: "VideoLessonsCellHighlighted")
cell.selectedBackgroundView = backgroundView
cell.textLabel?.font = UIFont(name: "Helvetica-Bold", size: 14)
cell.detailTextLabel?.font = UIFont(name: "Helvetica", size: 12)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
playVideo(at: indexPath)
}
func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.contentView.backgroundColor = UIColor(named: "VideoLessonsCellHighlighted")
cell.textLabel?.highlightedTextColor = UIColor(named: "textHighlighted")
cell.detailTextLabel?.highlightedTextColor = UIColor(named: "textHighlighted")
}
}
func tableView(_ tableView: UITableView, didUnhighlightRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.contentView.backgroundColor = nil
}
}
func playVideo(at indexPath: IndexPath){
let selectedVideo = viewModel.videos[indexPath.row]
let videoURL = URL(string: selectedVideo.fileURL)
player = AVPlayer(url: videoURL!)
playerViewController.player = player
self.present(playerViewController, animated: true, completion: {
self.playerViewController.player?.play()
})
}
}
This is the viewModel which I believe should download images and pass to the tableview cells.
import Foundation
import FirebaseFirestore
class VideosViewModel: ObservableObject {
#Published var videos = [Video]()
private var db = Firestore.firestore()
func fetchData() {
db.collection("videos").addSnapshotListener { [self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No Documents")
return
}
self.videos = documents.map { (queryDocumentSnapshot) -> Video in
let data = queryDocumentSnapshot.data()
let name = data["name"] as? String ?? ""
let imageName = data["imageName"] as? String ?? ""
let lessonName = data["lessonName"] as? String ?? ""
let fileURL = data["fileURL"] as? String ?? ""
print(data)
return Video(name: name , imageName: imageName , lessonName: lessonName, fileURL: fileURL )
}
}
}
}
And this the struct of the video
import Foundation
struct Video {
var name: String
var imageName: String
var lessonName: String
var fileURL: String
}
Does an image with that 'video.imageName' exist within the app?
let image = UIImage(named: video.imageName)
cell.imageView?.image = image
you should be download from firebase storage.
use to
guard let url = URL(string: video.imageName) else {return}
URLSession.shared.dataTask(with: URL, completionHandler: (Data?, URLResponse?, Error?) -> Void)
the first answer is one option however you will encounter the frozen screen while scrolling the UITableView.
if you want to void that. Then you can use SDWebImage SDK
import UIKit
import SDWebImage
class ListOfVideoLessonsTableViewController {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let video = viewModel.videos[indexPath.row]
tableView.tableFooterView = UIView()
cell.textLabel?.text = video.name
cell.detailTextLabel?.text = video.lessonName
cell.accessoryType = .disclosureIndicator
let image = UIImage(named: video.imageName)
cell.imageView?.sd_imageIndicator = SDWebImageActivityIndicator.gray
cell.imageView ?.sd_setImage(with: URL(string: stringWithoutWhitespace), placeholderImage: UIImage())
let backgroundView = UIView()
backgroundView.backgroundColor = UIColor(named: "VideoLessonsCellHighlighted")
cell.selectedBackgroundView = backgroundView
cell.textLabel?.font = UIFont(name: "Helvetica-Bold", size: 14)
cell.detailTextLabel?.font = UIFont(name: "Helvetica", size: 12)
return cell
}
}

Issue with TableView and Firebase implementation (Swift)

I want to connect my TableView to what I query from Firestore. The query works, but I can't get the TableView to show the content. Right now its just a blank tableView. The TableViewCell file also has no issues, since it worked before without the firebase implementation (The Cell is registered correctly).
I suspect that the issue is in cellForRowAt and tried played around in there, but couldn't get anything to work.
Can you find the issue?
import UIKit
import Firebase
class popularViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet var table: UITableView!
var texttt = [TextPost]()
override func viewDidLoad() {
super.viewDidLoad()
gettingPosts()
table.register(textTableViewCell.nib(), forCellReuseIdentifier: textTableViewCell.identifier)
table.delegate = self
table.dataSource = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
gettingPosts()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let alle = models.count + texttt.count
return alle
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: textTableViewCell.identifier, for: indexPath) as! textTableViewCell
cell.configure(with: texttt[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 300
}
func gettingPosts(){
let db = Firestore.firestore()
let postsRef = db.collection("posts")
postsRef.addSnapshotListener { (querySnapshot, error) in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added){
let data = diff.document.data()
let Name = data["username"] as! String
let text = data["description"] as! String
let likes = data["likes"] as! Int
let typ = data["postType"] as! Int
let pfp = data["profileImage"] as! String
let uid = data["uid"] as! String
let pic = data["picture"]
let time = data["time"] as! String
if typ == 0{ // Text post
let dasDing = TextPost(numberOfComments: 0, username: Name, timestampName: time, userImageName: pfp, textName: text)
self.texttt.append(dasDing)
}
}
}
}
}
}
struct TextPost {
let numberOfComments: Int
let username: String
let timestampName: String
let userImageName: String
let textName: String
}
You need to reload data once you get data from firebase
func gettingPosts(){
let db = Firestore.firestore()
let postsRef = db.collection("posts")
postsRef.addSnapshotListener { (querySnapshot, error) in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added){
let data = diff.document.data()
let Name = data["username"] as! String
let text = data["description"] as! String
let likes = data["likes"] as! Int
let typ = data["postType"] as! Int
let pfp = data["profileImage"] as! String
let uid = data["uid"] as! String
let pic = data["picture"]
let time = data["time"] as! String
if typ == 0{ // Text post
let dasDing = TextPost(numberOfComments: 0, username: Name, timestampName: time, userImageName: pfp, textName: text)
self.texttt.append(dasDing)
}
}
}
DispatchQueue.main.async {
tableView.reloadData()
}
}
}

How to cache the image in swift using SDWebImage?

I am fetching the image from firebase storage and want to cache it using SDWebImage, but in my ViewController it gets keep on downloaded. Please guide me how to cache the images
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! demoCell
cell.textDemo.text = images[indexPath.row]
var images_list = [String]()
images_list.append(images[indexPath.row])
images_list.append(images[indexPath.row] + "1")
let storage = Storage.storage().reference()
var imagesarray = [URL]()
for x in images_list{
let storageRef = storage.child("images/\(x).jpg")
storageRef.downloadURL { (url, error) in
if let error = error{
print(error.localizedDescription)
}
else if let downloadURL = url?.absoluteString{
cell.imageDemo.image = SDImageCache.shared().imageFromCache(forKey: downloadURL)
print("Image Cached")
}
else{
cell.imageDemo.sd_setImage(with: url!, completed: nil)
}
}
}
return cell
}
Rather than writing :
cell.imageDemo.image = SDImageCache.shared().imageFromCache(forKey: downloadURL)
Try this :
cell.imageDemo.sd_setImage(with: URL(string: downloadURL), placeholderImage: UIImage(named: "Your Default Image"))
SDWebImage will handle the caching part here.
This was my solution to the issue
var value : Any?
var vc = ViewController()
var images = [String]()
var downloads_array = [URL]()
override func viewDidLoad() {
super.viewDidLoad()
getImageNames()
downloadImages()
}
func downloadImages(){
let storage = Storage.storage().reference()
for x in images{
let storageRef = storage.child("images/\(x).jpg")
storageRef.downloadURL { (url, error) in
if let error = error{
print(error.localizedDescription)
}
else{
self.downloads_array.append(url!)
self.tableView.reloadData()
}
}
}
}
func getImageNames(){
images = vc.images
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return downloads_array.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! demoCell
cell.textDemo.text = images[indexPath.row]
if let downloadURL = SDImageCache.shared().imageFromCache(forKey: downloads_array[indexPath.row].absoluteString){
cell.imageDemo.image = downloadURL
}
else{
cell.imageDemo.sd_setImage(with: downloads_array[indexPath.row], completed: nil)
}
return cell
}

How to use swiftyJson to get arrayvalue in TableView?

I use swiftyJson to parse data and, but do not know how to parse array.
Here is code.
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var json:JSON = JSON.null
var urlSession = URLSession(configuration: .default)
#IBOutlet weak var myTableView: UITableView!
var pokamon = [[String:AnyObject]]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pokamon.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
var dict = pokamon[indexPath.row]
cell.nameLbl.text = dict["name"] as? String
cell.typesLbl.text = dict["types"]?[0] as? String
cell.hpLbl.text = dict["hp"] as? String
cell.subtypeLbl.text = dict["subtype"] as? String
if let image = URL(string: dict["imageUrl"] as! String){
let task = urlSession.downloadTask(with: image) { (url, repsponse, error) in
if error != nil{
print("sorry")
return
}
if let okURL = url{
do{
let downloadImage = UIImage(data: try Data(contentsOf: okURL))
DispatchQueue.main.async {
cell.myImage.image = downloadImage
}
}catch{
print("error")
}
}
}
task.resume()
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 224
}
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self
myTableView.dataSource = self
Alamofire.request("https://api.pokemontcg.io/v1/cards").responseJSON(completionHandler: { response in
if response.result.isSuccess {
let json:JSON = try! JSON(data: response.data!)
let swiftyJsonVar = JSON(response.result.value!)
if let resData = swiftyJsonVar["cards"].arrayObject{
self.pokamon = resData as! [[String:AnyObject]]
}
if self.pokamon.count > 0{
self.myTableView.reloadData()
}
} else {
print("error: \(response.error)")
}
})
}
}
From cellForRowAt in tableView, it shows "Ambiguous use of 'subscript'" with below code, have no idea how to solve it. The rest like "name", "hp", and "subtype" are no problem!
cell.typesLbl.text = dict["types"]?[0] as? String
Could anyone help me with this error?
Thanks!
The compiler must know the type of any subscripted object. Tell the compiler that you expect an array of strings.
cell.typesLbl.text = (dict["types"] as? [String])?.first
In Swift 3+ all JSON values are Any not AnyObject so declare the array
var pokamon = [[String:Any]]()

How do I use SDImageView to download URL within a TableViewCell?

I am trying to use the SDImageView Cocoapod with a table view to retrieve a URL from a database and turning it into a viewable image. When I use the code bellow I don't get images in return, what is wrong with my code? Thanks!
var posts = [postStruct]()
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 }
let post = postStruct(title: title, 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 imageView = cell?.viewWithTag(200) as! UIImageView
imageView.sd_setImage(with: URL(string: "downloadURL"), placeholderImage: UIImage(named: "placeholder.png"))
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].title
return cell!
}
You need to change SDWebimage syntax as ->
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 }
downloadURL = value["Download URL"] as? String ?? ""
let post = postStruct(title: title, 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 imageView = cell?.viewWithTag(200) as! UIImageView
imageView.sd_setImage(with: URL(string: downloadURL), placeholderImage: UIImage(named: "placeholder.png"))
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].title
return cell!
}
Where downloadURL is url String.
You first need to pick the postStruct from the array and then the downloadURL. Change your override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell method with this.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let imageView = cell?.viewWithTag(200) as! UIImageView
let post = self.posts[indexPath.row];
imageView.sd_setImage(with: URL(string: post.downloadURL), placeholderImage: UIImage(named: "placeholder"))
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].title
return cell!
}