I'm creating an iOS application which has many pictures on it. I therefore decided to use a database to store the pictures on and retrieve from the database. I have inputted the pictures manually through the Firebase site.
Here is the code I currently have:
import UIKit
import Firebase
class F_A_1: UIViewController {
#IBOutlet weak var imageViewer: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
let database = FIRDatabase.database().reference()
let storage = FIRStorage.storage().reference()
let animalref = FIRStorage.storage().reference().child("animal/bird.png")
animalref.dataWidthMaxSize(1*1000*1000){ (date, error) in
if error = nill {
print(data)
self.imageViewer.image = UIImage(data: data!)
} else {
print(error?.localizedDescription)
}
}
}
This is giving me an error which I cannot fix.
Thanks in advance :)
import UIKit
import Firebase
class F_A_1: UIViewController {
#IBOutlet weak var imageViewer: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
let database = FIRDatabase.database().reference()
let storage = FIRStorage.storage().reference()
let animalref = FIRStorage.storage().reference().child("animal/bird.png")
func nameThisWhatYouWant() {
animalref.data(withMaxSize: 1*1000*1000) { (data, error) in
if error == nil {
print(data)
} else {
print(error?.localizedDescription)
}
}
}
}
i have tried this but it gives a sigbart error
Well I have never worked with Firebase and don't know how to go about connecting it, but you have an issue where you are doing logic in your app.
class F_A_1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
let database = FIRDatabase.database().reference()
let storage = FIRStorage.storage().reference()
let animalref = FIRStorage.storage().reference().child("animal/bird.png")
func nameThisWhatYouWant() {
animalref.dataWidthMaxSize(1*1000*1000){ (date, error) in
if error = nill {
print(data)
self.imageViewer.image = UIImage(data: data!)
} else{
print(error?.localizedDescription)
}
}
}
}
I added a function called nameThisWhatYouWant where you should be handling your data, and sending it off to Firebase. As far as working with Firebase, go here to see how to set up Firebase by working with Cocoa Pods, then allowing you to import Firebase. As far as actually being able to send your data off, they have tons of documentation and examples to follow to read through.
You are comparing the error to nill instead of nil.
Where are you getting your error?
Related
UPDATE at the bottom.
I have followed the UIKit section of this Apple iOS Dev Tutorial, up to and including the Saving New Reminders section. The tutorials provide full code for download at the beginning of each section.
But, I want to get FirebaseFirestore involved. I have some other Firestore projects that work, but I always thought that I was doing something not quite right, so I'm always looking for better examples to learn from.
This is how I found Peter Friese's 3-part YT series, "Build a To-Do list with Swift UI and Firebase". While I'm not using SwiftUI, I figured that the Firestore code should probably work with just a few changes, as he creates a Repository whose sole function is to interface between app and Firestore. No UI involved. So, following his example, I added a ReminderRepository.
It doesn't work, but I'm so close. The UITableView looks empty but I know that the records are being loaded.
Stepping through in the debugger, I see that the first time the numberOfRowsInSection is called, the data hasn't been loaded from the Firestore, so it returns 0. But, eventually the code does load the data. I can see each Reminder as it's being mapped and at the end, all documents are loaded into the reminderRepository.reminders property.
But I can't figure out how to get the loadData() to make the table reload later.
ReminderRepository.swift
class ReminderRepository {
let remindersCollection = Firestore.firestore()
.collection("reminders").order(by: "date")
var reminders = [Reminder]()
init() {
loadData()
}
func loadData() {
print ("loadData")
remindersCollection.addSnapshotListener { (querySnapshot, error) in
if let querySnapshot = querySnapshot {
self.reminders = querySnapshot.documents.compactMap { document in
do {
let reminder = try document.data(as: Reminder.self)
print ("loadData: ", reminder?.title ?? "Unknown")
return reminder
} catch {
print (error)
}
return nil
}
}
print ("loadData: ", self.reminders.count)
}
}
}
The only difference from the Apple code is that in the ListDataSource.swift file, I added:
var remindersRepository: ReminderRepository
override init() {
remindersRepository = ReminderRepository()
}
and all reminders references in that file have been changed to
remindersRepository.reminders.
Do I need to provide a callback for the init()? How? I'm still a little iffy on the matter.
UPDATE: Not a full credit solution, but getting closer.
I added two lines to ReminderListViewController.viewDidLoad() as well as the referenced function:
refreshControl = UIRefreshControl()
refreshControl?.addTarget(self, action: #selector(refreshTournaments(_:)), for: .valueChanged)
#objc
private func refreshTournaments(_ sender: Any) {
tableView.reloadData()
refreshControl?.endRefreshing()
}
Now, when staring at the initial blank table, I pull down from the top and it refreshes. Now, how can I make it do that automatically?
Firstly create some ReminderRepositoryDelegate protocol, that will handle communication between you Controller part (in your case ReminderListDataSource ) and your model part (in your case ReminderRepository ). Then load data by delegating controller after reminder is set. here are some steps:
creating delegate protocol.
protocol ReminderRepositoryDelegate: AnyObject {
func reloadYourData()
}
Conform ReminderListDataSource to delegate protocol:
class ReminderListDataSource: UITableViewDataSource, ReminderRepositoryDelegate {
func reloadYourData() {
self.tableView.reloadData()
}
}
Add delegate weak variable to ReminderRepository that will weakly hold your controller.
class ReminderRepository {
let remindersCollection = Firestore.firestore()
.collection("reminders").order(by: "date")
var reminders = [Reminder]()
weak var delegate: ReminderRepositoryDelegate?
init() {
loadData()
}
}
set ReminderListDataSource as a delegate when creating ReminderRepository
override init() {
remindersRepository = ReminderRepository()
remindersRepository.delegate = self
}
load data after reminder is set
func loadData() {
print ("loadData")
remindersCollection.addSnapshotListener { (querySnapshot, error) in
if let querySnapshot = querySnapshot {
self.reminders = querySnapshot.documents.compactMap { document in
do {
let reminder = try document.data(as: Reminder.self)
print ("loadData: ", reminder?.title ?? "Unknown")
delegate?.reloadYourData()
return reminder
} catch {
print (error)
}
return nil
}
}
print ("loadData: ", self.reminders.count)
}
}
Please try changing var reminders = [Reminder]() to
var reminders : [Reminder] = []{
didSet {
self.tableview.reloadData()
}
}
I'm using Firestore together with Swift.
I have a singleton data class UserManager. I call this from my different ViewControllers to get data to populate my tableviews. I want the tableviews to automatically update when the collections are updated so I need to use a SnapshotListener. Everything works fine but I'm not sure how to detach from the listener when the Viewcontroller is closed.
In the singleton class I have methods like this below. The method gives a list of users and will be called from several different places around my app.
I also want to give back a reference to the listener so that I can detach from it when the Viewcontroller is closed. But I can't get it working. The below solution gives compiler error.
I've been trying to look at the reference, for example here
https://firebase.google.com/docs/firestore/query-data/listen but I need to get it working when the data is loaded in a singleton class instead of directly in the Viewcontroller. What is the way to go here?
In UserManager:
func allUsers(completion:#escaping ([User], ListenerRegistration?)->Void) {
let listener = db.collection("users").addSnapshotListener { querySnapshot, error in
if let documents = querySnapshot?.documents {
var users = [User]()
for document in documents {
let user = User(snapshot: document)
users.append(user)
}
completion(users, listener)
}
}
}
In ViewController:
override func viewDidLoad() {
super.viewDidLoad()
UserManager.shared.allUsers(completion: { (users, listener) in
self.users = users
self.listener = listener
self.tableView.reloadData()
})
}
deinit {
self.listener.remove()
}
I guess the compiler error that you see is referring to the fact that you are using listener into it's own defining context.
Try this for a change:
In UserManager:
func allUsers(completion:#escaping ([User])->Void) -> ListenerRegistration? {
return db.collection("users").addSnapshotListener { querySnapshot, error in
if let documents = querySnapshot?.documents {
var users = [User]()
for document in documents {
let user = User(snapshot: document)
users.append(user)
}
completion(users)
}
}
}
In ViewController:
override func viewDidLoad() {
super.viewDidLoad()
self.listener = UserManager.shared.allUsers(completion: { (users) in
self.users = users
self.tableView.reloadData()
})
}
deinit {
self.listener.remove()
}
I think that getDocument instead of addSnapshotListener is what you are looking for.
Using this method the listener is automatically detached at the end of the request...
It will be something similar to
func allUsers(completion:#escaping ([User])->Void) {
db.collection("users").getDocument { querySnapshot, error in
if let documents = querySnapshot?.documents {
var users = [User]()
for document in documents {
let user = User(snapshot: document)
users.append(user)
}
completion(users)
}
} }
I'm having a bit of issue implying my code. I'm trying to build a chat app and I'm getting one lousy error. I was wondering if anyone could help me fix it or tell me what I'm doing wrong?
import UIKit
import Firebase
class LoginViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
if nameField?.text != "" { // 1
FIRAuth.auth()?.signInAnonymously(completion: { (user, error) in // 2
if let err = error { // 3
print(err.localizedDescription)
return
}
self.performSegue(withIdentifier: "LoginToChat", sender: nil) // 4
})
}
}
I think as textfield outlet is always optional, so you are getting this error.Take value of textfield in variable then compare it. Let say code as below:
let stringTextValue = nameField.text! as String
if stringTextValue != "" {
Why when I'am adding new data to firebase database appears duplicated in my app , but when I pull down to make refresh its back again not duplicated , I put a Screenshot of how data appears in my project simulator .This is how data appears in my project
This is my Code.
import UIKit
import FirebaseDatabase
import SDWebImage
import FirebaseAuth
class ViewController: UIViewController , UITableViewDataSource , UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var Ref:FIRDatabaseReference?
var Handle:FIRDatabaseHandle?
var myarray = [Posts]()
var refresh = UIRefreshControl()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
refresh.addTarget(self, action: #selector(ViewController.loadData), for: .valueChanged)
tableView.addSubview(refresh)
}
override func viewDidAppear(_ animated: Bool) {
loadData()
}
func loadData(){
self.myarray.removeAll()
Ref=FIRDatabase.database().reference()
Handle = Ref?.child("Posts").queryOrdered(byChild: "Des").queryEqual(toValue: "11").observe(.childAdded ,with: { (snapshot) in
if let post = snapshot.value as? [String : AnyObject]{
let img = Posts()
img.setValuesForKeys(post)
self.myarray.append(img)
print("##############################\(img)")
self.tableView.reloadData()
}
})
self.refresh.endRefreshing()
}
It appears self.refresh.endRefreshing() is being called outside the Firebase closure.
This is bad as that code will run before Firebase returns the data and reloads the tableView data.
Remember that Firebase is asynchronous and it takes time for the internet to get the data from the Firebase server to your app, so in-line code will usually execute before the code in the closure.
Also, the Handle is unneeded in this case unless you are using it elsewhere.
Ref?.child("Posts").queryOrdered(byChild: "Des").queryEqual(toValue: "11")
.observe(.childAdded ,with: { (snapshot) in
if let post = snapshot.value as? [String : AnyObject]{
let img = Posts()
img.setValuesForKeys(post)
self.myarray.append(img)
print("##############################\(img)")
self.tableView.reloadData()
}
self.refresh.endRefreshing()
})
Also, you may consider just removing the refreshing entirely as when you add an observer to a Firebase node (.childAdded) any time a child it added, your tableview will automatically refresh due to the code executed in the closure.
I'm trying to retrieve data from my Firebase database, but I'm stuck at the database reference returning nil. However, I successfully can retrieve the UserID, so I don't think it's not properly connected to my database:
import UIKit
import Firebase
import FirebaseDatabaseUI
class LoggedInViewController: UIViewController {
var ref: FIRDatabaseReference!
let userid = FIRAuth.auth()?.currentUser?.uid
override func viewDidLoad() {
super.viewDidLoad()
UIDLabel.text = String((userid)!) // The Label shows the UID
reflabel.text = String(ref) // The Label shows "nil"
}
}
My goal is to retrieve data from the path using this method:
func getQuery() -> FIRDatabaseQuery {
let myTopPostsQuery = (ref.child("user-posts")).child(getUid()))
return myTopPostsQuery
}
But obviously this doesn't work since I'm getting an error because of the nil reference.
In viewDidLoad use FIRDatabase.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
self.ref = FIRDatabase.database().reference()
//....
}