fetching images from firebase in swift 3 - uitableview

I am using Firebase as a backend for my project.I need to display text and image from Firebase database in tableview. It displays only text in my tableview and shows error while retrieving image as "cannot convert value of type 'UIImage?' to expected argument type 'String'".Please anyone, who can correct me. My code is given below:
//App Delegate
import UIKit
import CoreData
import Firebase
import FirebaseDatabase
import FirebaseStorage
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FIRApp.configure()
return true
}
//View Controller
import UIKit
import Firebase
import FirebaseDatabase
import FirebaseStorage
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var ref: FIRDatabaseReference? = nil
var refHandle: UInt? = nil
var userList = [User]()
#IBOutlet var tblHome: UITableView!
let textCellIdentifier = "TextCell"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tblHome.delegate = self
tblHome.dataSource = self
self.ref = FIRDatabase.database().reference()
print("ref = ",ref)
fetchUsers()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("userList is", userList.count)
return userList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: textCellIdentifier, for: indexPath) as! HomeCell
cell.profname.text = userList[indexPath.row].imageName
if let url = URL.init(string: userList[indexPath.row].imageUrl) { // Error: cannot convert value of type 'UIImage?' to expected argument type 'String'
cell.profImage.image.downloadedFrom(url: url)
}
return cell
}
func fetchUsers() {
refHandle = ref?.child("Users").observe(.childAdded, with: { (snapshot) in
print("refHandle =", self.refHandle)
if let dictionary = snapshot.value as? [String: AnyObject] {
print("dictionary =", dictionary)
let user = User()
user.setValuesForKeys(dictionary)
self.userList.append(user)
print("userlist is", user)
DispatchQueue.main.async(execute: {
self.tblHome.reloadData()
})
}
})
}
}
extension UIImageView {
func downloadedFrom(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { () -> Void in
self.image = image
}
}.resume()
}
// func downloadedFrom(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
// guard let url = URL(string: link) else { return }
// downloadedFrom(url: url, contentMode: mode)
// }
}
//Home Cell
import UIKit
class HomeCell: UITableViewCell {
#IBOutlet var profImage: UIImageView!
#IBOutlet var profname: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
//User
import UIKit
class User: NSObject {
var imageName: String?
var imageUrl: UIImage?
}

According to the documentation, URL() takes a String and you are passing a UIImage to it.
Inside your User class, imageUrl is of type UIImage? and you are passing it into the URL constructor even though it is expecting a String.
You can solve this by changing your User class to this:
class User: NSObject
{
var imageName: String?
var imageUrl: String? // pass this to the URL constructor
var image: UIImage? // this stores the actual image
}

Related

I cannot get the data from the Sqlite database

I cannot get the data from the Sqlite database.
It doesn't print anything for me. I checked the connection to the db is made and the database contains the data.
It gives no error but does not print anything.
When I start the app the tableView contains no records.
In console print this:
API call with NULL database connection pointer
misuse at line 139466 of [d24547a13b]
prepare: out of memory
// AppDelegate.swift
import UIKit
import SQLite3
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DataManager.dataManager.dbOpaquePointer = DataManager.dataManager.openDatabase(dbName: "DB.sqlite")
return true
}
}
// TableWord.swift
import Foundation
class TableWord{
var idword: Int = 0
var word: String = ""
var features: String = ""
var number: Int = 0
init(idword: Int, word: String, features: String, number: Int) {
self.idword = idword
self.word = word
self.features = features
self.number = number
}
}
// DataManager.swift
import UIKit
import SQLite3
class DataManager {
static let dataManager = DataManager()
var dbOpaquePointer: OpaquePointer?
// OPEN
func openDatabase(dbName: String) -> OpaquePointer? {
let documentDirectory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileUrl = documentDirectory?.appendingPathComponent(dbName).relativePath
guard let part1DbPath = fileUrl else {
print("part1DbPath is nil.")
return nil
}
if sqlite3_open(part1DbPath, &dbOpaquePointer) == SQLITE_OK {
print(“Open database in \(part1DbPath)")
return dbOpaquePointer
} else {
print("\(Errors.SQLiteError.openDatabase)")
}
return dbOpaquePointer
}
// SELECT
func selectTableWord() -> [TableWord] {
let querySelectTableWord = "SELECT * FROM tableword;"
var dbOpaquePointer2: OpaquePointer? = nil
var dataTableWord: [TableWord] = []
if sqlite3_prepare_v2(dbOpaquePointer, querySelectTableWord, -1, &dbOpaquePointer2, nil) == SQLITE_OK {
while (sqlite3_step(dbOpaquePointer2) == SQLITE_ROW) {
let idword = sqlite3_column_int(dbOpaquePointer2, 0)
guard let queryResultCol1 = sqlite3_column_text(dbOpaquePointer2, 1) else {
print("\(Errors.SQLiteError.queryNil)")
return dataTableWord
}
let word = String(cString: queryResultCol1)
guard let queryResultCol2 = sqlite3_column_text(dbOpaquePointer2, 2) else {
print("\(Errors.SQLiteError.queryNil)")
return dataTableWord
}
let features = String(cString: queryResultCol2)
let number = sqlite3_column_int(dbOpaquePointer2, 3)
dataTableWord.append(TableWord(idword: Int(idword), word: word, features: features, number: Int(number)))
}
} else {
let errorMessage = String(cString: sqlite3_errmsg(dbOpaquePointer2))
print("\(Errors.SQLiteError.prepare): \(errorMessage)")
}
sqlite3_finalize(dbOpaquePointer2)
return dataTableWord
}
}
// ViewController.swift
import UIKit
import SQLite3
class ViewController: UIViewController, UITextViewDelegate, UITableViewDataSource {
#IBOutlet weak var resultTableView: UITableView!
var db: DataManager = DataManager()
var dataTableWord: [TableWord] = []
override func viewDidLoad() {
super.viewDidLoad()
dataTableWord = db.selectTableWord()
searchTextView.delegate = self
resultTableView.dataSource = self
}
//MARK: - UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataTableWord.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "idCell1Main1", for: indexPath) as! TableViewCell
cell.cellLabelNumber.text = String(dataTableWord[indexPath.row].idword)
cell.cellLabelTitle.text = dataTableWord[indexPath.row].word
cell.cellLabelSubtitle.text = dataTableWord[indexPath.row].features
return cell
}
}
In viewDidLoad you call
var db: DataManager = DataManager()
Which means you are creating a new instance of DataManager instead of using the instance you created in your app delegate and that has an open database connection
You should always access your database using the static property you have created
let db = DataManager.databaseManager
To avoid mistakes like this you can add a private init to your class
private init() {}
this way you can only access it via DataManager.databaseManager

TableView not displaying Firebase data in xcode (swiift)

I am using Xcode 11 to try and display 'post' data in Firebase in a tableview, and I have tested calling the information with prints, which works.
This is my code for the table view controller:
import UIKit
import Firebase
import FirebaseDatabase
import SwiftKeychainWrapper
import FirebaseAuth
class FeedVC: UITableViewController {
var currentUserImageUrl: String!
var posts = [Post]()
var selectedPost: Post!
override func viewDidLoad() {
super.viewDidLoad()
getUsersData()
getPosts()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getUsersData(){
guard let userID = Auth.auth().currentUser?.uid else { return }
Database.database().reference().child("users").child(userID).observeSingleEvent(of: .value) { (snapshot) in
if let postDict = snapshot.value as? [String : AnyObject] {
self.tableView.reloadData()
}
}
}
func getPosts() {
Database.database().reference().child("textPosts").observeSingleEvent(of: .value) { (snapshot) in
guard let snapshot = snapshot.children.allObjects as? [DataSnapshot] else { return }
self.posts.removeAll()
for data in snapshot.reversed() {
guard let postDict = data.value as? Dictionary<String, AnyObject> else { return }
let post = Post(postKey: data.key, postData: postDict)
print(DataSnapshot.self)
self.posts.append(post)
}
self.tableView.reloadData()
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "postCell") as? PostCell else { return UITableViewCell() }
cell.configCell(post: posts[indexPath.row])
return cell
}
}
This is the code for the postCell:
import UIKit
import Firebase
import FirebaseStorage
import FirebaseDatabase
import SwiftKeychainWrapper
class PostCell: UITableViewCell {
#IBOutlet weak var userImg: UIImageView!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var postText: UILabel!
#IBOutlet weak var commentBtn: UIButton!
var post: Post!
let currentUser = KeychainWrapper.standard.string(forKey: "uid")
func configCell(post: Post) {
self.post = post
self.username.text = post.username
self.postText.text = post.postText
print(self.post)
print(self.post.username)
let ref = Storage.storage().reference(forURL: post.userImg)
ref.getData(maxSize: 100000000, completion: { (data, error) in
if error != nil {
print("couldnt load img")
} else {
if let imgData = data {
if let img = UIImage(data: imgData){
self.userImg.image = img
}
}
}
})
}}
and this is for the Post:
import Foundation
import Firebase
import FirebaseDatabase
class Post {
private var _username: String!
private var _userImg: String!
private var _postText: String!
private var _postKey: String!
private var _postRef: DatabaseReference!
var username: String {
return _username
}
var userImg: String {
return _userImg
}
var postText: String {
return _postText
}
var postKey: String {
return _postKey
}
init(postText: String, username: String, userImg: String) {
_postText = postText
_username = username
_userImg = userImg
}
init(postKey: String, postData: Dictionary<String, AnyObject>) {
_postKey = postKey
if let username = postData["username"] as? String {
_username = username
}
if let userImg = postData["userImg"] as? String {
_userImg = userImg
}
if let postText = postData["postText"] as? String {
_postText = postText
}
_postRef = Database.database().reference().child("posts").child(_postKey)
}
}
I have been stuck on this for a while and any help would be much appreciated!
Did you check the data source methods are getting called?
Also please confirm if your cells have height.

Swift/IOS: How do I get PDF from Firebase and display it in a webkit?

In firebase, I have a storage with PDF's and a collection that has a reference to the stored PDF, along with the name of the PDF.
I'm retrieving the collection documents and display the headline in a tableview.
I want to be able to click the tableview cell, then display the pdf in a webkit view in another viewcontroller.
How do I fetch the PDF in storage and display it in webkit view?
My code so far:
Handler
class PDFHandler {
static let db = Firestore.firestore()
static let storage = Storage.storage()
static var list = [PDF]()
static var downloadURL : String?
static func Create(title: String, url: String){
}
static func startListener(tableView: UITableView){
print("Listening has begun")
db.collection("PDF").addSnapshotListener { (snap, error) in
if error == nil{
self.list.removeAll()
for pdf in snap!.documents{
let map = pdf.data()
let head = map["Headline"] as! String
let url = map["URL"] as? String ?? "empty"
let newPDF = PDF(id: pdf.documentID, headline: head, url: url)
print(newPDF)
self.list.append(newPDF)
}
DispatchQueue.main.async {
tableView.reloadData()
}
}
}
}
static func downloadPdfs(pdfID: String, pdfurl: String){
print("Download initiated")
let pdfRef = storage.reference(withPath: pdfID)
pdfRef.getData(maxSize: 99999999999) { (data, error) in
if error == nil{
print("Success downloading PDF")
DispatchQueue.main.async {
}
}else{
print("Error fetching pdf")
}
}
}
static func getSize() -> Int{
return list.count
}
static func getPDFat(index: Int) -> PDF{
return list[index]
}
}
Tableview
class ReportViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
PDFHandler.startListener(tableView: tableView)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return PDFHandler.getSize()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PDFCell")
cell?.textLabel?.text = PDFHandler.getPDFat(index: indexPath.row).headline
return cell!
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PDFViewController{
destination.rowNumber = tableView.indexPathForSelectedRow!.row
}
}
}
PDFView VC
import UIKit
import WebKit
class PDFViewController: UIViewController, WKUIDelegate {
#IBOutlet weak var pdfview: WKWebView!
var rowNumber = 0
var url = ""
override func viewDidLoad() {
super.viewDidLoad()
let myUrl = URL(string: "")
let myRequest = URLRequest(url: myUrl!)
pdfview.load(myRequest)
}
override func loadView() {
let webConfig = WKWebViewConfiguration()
pdfview = WKWebView(frame: .zero, configuration: webConfig)
pdfview.uiDelegate = self
view = pdfview
}
}

Swift - Extra argument 'image' in call

I am trying to turn the following which works
tempNews.append(News(title: $0.title))
into
tempNews.append(News(title: $0.title, image: $0.image))
However when I type it I get the following error Extra argument 'image' in call now the JSON file does have image so I know its not the JSON return that is causing this error. So It must be something else either in
struct NewsData: Decodable{
let news: [articalData]
}
struct articalData: Decodable{
let title: String
let image: String
}
or
import Foundation
//import UIKit
class News {
// var image: UIImage
var title: String
var image: String
init(title: String) {
self.image = image
self.title = title
}
}
Here is the full view controller script
//
// NewsViewController.swift
// DRN1
//
// Created by Russell Harrower on 26/11/19.
// Copyright © 2019 Russell Harrower. All rights reserved.
//
import UIKit
import Foundation
struct NewsData: Decodable{
let news: [articalData]
}
struct articalData: Decodable{
let title: String
let image: String
}
class NewsViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var news: [News] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func viewDidAppear(_ animated: Bool) {
self.tabBarController?.navigationItem.title = "News"
self.newsfetch { [weak self] news in
guard let news = news else { return }
self?.news = news
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
}
func newsfetch(_ completionHandler: #escaping ([News]?)->Void){
let jsonURLString = "https://api.drn1.com.au/api-access/news"
guard let feedurl = URL(string: jsonURLString) else { return }
URLSession.shared.dataTask(with: feedurl){ (data,response,err)
in
guard let news = data else { return }
do {
let newsdata = try JSONDecoder().decode(NewsData.self, from: news)
var tempNews: [News] = []
newsdata.news.forEach(){
var strUrl = $0.image
strUrl = strUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
tempNews.append(News(title: $0.title, image: strUrl))
}
completionHandler(tempNews)
} catch let jsonErr {
print("error json ", jsonErr)
completionHandler(nil)
}
}.resume()
}
}
extension NewsViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return news.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let newsa = news[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "NewsCell") as! NewsCell
cell.setNews(news: newsa)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "shownewsarticle", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? NewsArticleViewController{
destination.article = news[(tableView.indexPathForSelectedRow?.row)!]
tableView.deselectRow(at: tableView.indexPathForSelectedRow!, animated: true)
}
}
}
My first plan is to just get it to not throw that error, and once I have that working i'll use kingfisher to load the remote image.
class News {
// var image: UIImage
var title: String
var image: String
init(title: String){
self.image = image
self.title = title
}
}
The init method does not contain image. This is your issue
Change to
init(title: String,
image: String) {
self.image = image
self.title = title
}

Error: Cannot assign value of type 'String?' to type 'String?.Type'

I've got a couple a problem with this code. Here's my code and I don't understand why there is an error line 61 with cell.userID = self.user[indexPath.row].userID it says : Cannot assign value of type String? to type String?.Type. It's probably because in line 36 : if let uid = value["profilepicture.userID"] as? String. userID is in Firebase a child of profile picture but I don't know how to write that inside of value[]. Thanks for your answers.
// TableViewCell.swift
import UIKit
class FriendsTableViewCell: UITableViewCell {
#IBOutlet weak var userImage: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
var userID = String?.self
}
// ViewController.swift
import UIKit
import Firebase
class FriendsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableview: UITableView!
var user = [User]()
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func retrieveUsers() {
let ref = Database.database().reference()
ref.child("users").queryOrderedByKey().observeSingleEvent(of: .value, with: { DataSnapshot in
let users = DataSnapshot.value as! [String: AnyObject]
self.user.removeAll()
for (_, value) in users{
//let uid = Auth.auth().currentUser!.uid
if let uid = value["profilepicture.userID"] as? String{
if uid != Auth.auth().currentUser!.uid {
let userToShow = User()
if let fullName = value["username"] as? String , let imagePath = value["profilepicture.photoURL"] as? String {
userToShow.username = fullName
userToShow.imagePath = imagePath
userToShow.userID = uid
self.user.append(userToShow)
}
}
}
}
})
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableview.dequeueReusableCell(withIdentifier: "FriendsTableViewCell", for: indexPath) as! FriendsTableViewCell
cell.nameLabel.text = self.user[indexPath.row].username
cell.userID = self.user[indexPath.row].userID
cell.userImage.downloadImage(from: self.user[indexPath.row].imagePath!)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return user.count ?? 0
}
}
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()
}
}
Cannot assign value of type String? to type String?.Type.
Change
var userID = String?.self
To
var userID : String?

Resources