Access variable outside of function Swift - swift

How can I get the value from firstName from the inside:
func saveImage(name: String, postURL:URL, completion: #escaping ((_ url: URL?) -> ())){
//Get sspecific document from current user
let docRef = Firestore.firestore().collection("users").whereField("uid", isEqualTo: Auth.auth().currentUser?.uid ?? "")
var firstName = ""
// Get data
docRef.getDocuments { (querySnapshot, err) in
var firstName = ""
if let err = err {
print("ERROR: ")
print(err.localizedDescription)
return
} else if querySnapshot!.documents.count != 1 {
print("More than one documents or none")
} else {
let document = querySnapshot!.documents.first
let dataDescription = document?.data()
firstName = dataDescription?["firstname"] as! String
}
}
// This uploads the data
let dict = ["title": postDescriptionTitle.text!,
"description": postDescription.text!,
"Address": addressField.text!,
"Zipcode": zipcodeField.text!,
"timestamp": [".sv":"timestamp"],
"Author":firstName,
"postUrl": postURL.absoluteString]
as [String: Any]
self.ref.child("post").childByAutoId().setValue(dict)
}
It looks like it's out of scope, how can I store it or access it without storing it in another variable?
As you can see, I'm trying to upload the variable firstName to the database. So in this part:
"Author":firstName,
I should be getting the value so I can give it to Author

Just move the "upload data" part inside the completion block like this:
func saveImage(name: String, postURL:URL, completion: #escaping ((_ url: URL?) -> ())) {
//Get sspecific document from current user
let docRef = Firestore.firestore().collection("users").whereField("uid", isEqualTo: Auth.auth().currentUser?.uid ?? "")
// Get data
docRef.getDocuments { (querySnapshot, err) in
if let err = err {
print("ERROR: ")
print(err.localizedDescription)
return
} else if querySnapshot!.documents.count != 1 {
print("More than one documents or none")
} else {
let document = querySnapshot!.documents.first
let dataDescription = document?.data()
let firstName = dataDescription?["firstname"] as! String
// This uploads the data
let dict = ["title": self.postDescriptionTitle.text!,
"description": self.postDescription.text!,
"Address": self.addressField.text!,
"Zipcode": self.zipcodeField.text!,
"timestamp": [".sv":"timestamp"],
"Author": firstName,
"postUrl": postURL.absoluteString] as [String: Any]
self.ref.child("post").childByAutoId().setValue(dict)
}
}
}
Also for what are you using the completion argument in your saveImage function?

Related

getdocuments function not stopping

so I'm trying to get data from firestore using this functionL
static func getData() -> [Post]{
let db = Firestore.firestore()
var posts = [Post]()
db.collection("Posts").getDocuments { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error retreiving snapshots \(error!)")
return
}
for document in snapshot.documents{
posts.append(Post(t: document.data()["title"] as? String ?? "", a: document.data()["author"] as? String ?? "", pm: document.data()["priceMax"] as? Double ?? 0.0, c: document.data()["content"] as? String ?? ""))
print("New Post... ")
print(document.data())
print(posts.count)
print(posts[0].title)
}
return
}
print("test")
return posts
}
and from the print statements I can tell that it gets the data, but the function never ends. print("test") never runs, and thus the posts are never returned. How can I change this so that it returns the data?
Getting data from Firestore is an asynchronous call. Try this out:
func getData(completion: #escaping([Post], Error?) -> Void) {
let db = Firestore.firestore()
var posts = [Post]()
db.collection("Posts").getDocuments { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error retreiving snapshots \(error!)")
completion(posts, error)
}
for document in snapshot.documents{
posts.append(Post(t: document.data()["title"] as? String ?? "", a: document.data()["author"] as? String ?? "", pm: document.data()["priceMax"] as? Double ?? 0.0, c: document.data()["content"] as? String ?? ""))
print("New Post... ")
print(document.data())
print(posts.count)
print(posts[0].title)
}
completion(posts, error)
}
}
Then call the function like this:
getData { posts, error in
print("Posts: \(posts)")
}

firestore fetch subcollection

I'm trying to fetch subcollection of my users document with code below
func readData(){
let userId = Auth.auth().currentUser?.uid
self.db.collection("users/\(userId)/saved").getDocuments { (snapshot, err) in
if let err = err {
print("err")
}
if let userId != nil {
for document in snapshot!.documents {
let docId = document.documentID
let cty = document.get("city") as! String
let ccode = document.get("code") as! String
let countr = document.get("country") as! String
print(cty, ccode, countr,docId)
}
}
}
but my code doesn't print anything, I don't understand the problem, documents exsist, see picture below
You're using illegal syntax with the userId check in the snapshot return but the logic flow is the bigger problem. I would recommend you check if the user is signed in before grabbing the subcollection and checking if there is a viable snapshot instead of checking the state of authentication.
func readData() {
guard let userId = Auth.auth().currentUser?.uid else {
return
}
db.collection("users/\(userId)/saved").getDocuments { (snapshot, error) in
guard let snapshot = snapshot else {
if let error = error {
print(error)
}
return
}
for doc in snapshot.documents {
guard let city = doc.get("city") as? String,
let code = doc.get("code") as? String,
let country = doc.get("country") as? String else {
continue // continue document loop
}
let docId = doc.documentID
print(city, code, country, docId)
}
}
}

Struggling with basic Swift logic (Working with constants, variables and loops)

I want to use 2 Strings that I get with those two "loops" from firebase and use them in another "loop" to upload them with a bunch of other Information.
My problem is, that I somehow can't get the values of fullname and pfp that I downloaded, into the upload to firebase.
Any ideas on how to solve this issue?
func sendToFire(){
let combined = "\(userID)" + "\(number)"
let docRef = db.collection("posts").document(combined)
let description = self.textPost.text
let nameRef = db.collection("users").document(userID)
var fullname = ""
var pfp = ""
if fireImage == nil {
nameRef.getDocument { (document, error) in
if let document = document{
fullname = document.get("fullname") as! String
}else{
print("Coulnt get fullname")
}
}
nameRef.getDocument { (document, error) in
if let document = document{
pfp = document.get("profileimage") as! String
}else{
print("Couldn't get profileimage")
}
}
docRef.getDocument { (document, error) in
if let document = document, document.exists {
print("Post ID already taken")
} else {
print("Post Document gets created")
self.db.collection("posts").document(combined).setData([
"description": description!,
"likes": self.likes,
"postType": 0,
"profileImage": pfp,
"time": self.date,
"uid": self.userID,
"username": fullname
]) { err in
if let err = err {
print("Error writing document: \(err)")
} else {
print("Post Document successfully written!")
}
}
}
}
}
}
Add document.exists in the if let
nameRef.getDocument { (document, error) in
if let document = document, document.exists{
fullname = document.get("fullname") as! String
}else{
print("Coulnt get fullname")
}
}
nameRef.getDocument { (document, error) in
if let document = document, document.exists{
pfp = document.get("profileimage") as! String
}else{
print("Couldn't get profileimage")
}
}
Check the actual key names in the response fullname and profileimage.

Getting error "Variable used within its own initial value"

I am writing a new bit of code that allows a user to upload an image to a post that will be saved in Firebase database and storage. Then it will be shown in a UITableView on a separate Viewcontroller. On the part of code where I want to save the photo url to my database I am getting an error "Variable used within its own initial value". I have looked at other threads covering this issue and when I follow their advice I get a new set of errors. Here is what my code looks like and where I am getting the error. If there are any suggestions that would be great, thank you!
Here is code:
func postWithImageAndText() {
var imagesArray = [AnyObject]()
let postLength = textView.text.count
let numImages = imagesArray.count
let postRef = Database.database().reference().child("posts").childByAutoId()
let storageRef = Storage.storage().reference()
let uid = Auth.auth().currentUser
let pictureStorageRef = storageRef.child("users/profile/\(uid)/media")
let lowResImageData = (imagesArray[0] as! UIImage).jpegData(compressionQuality: 0.50)
if(postLength>0 && numImages>0)
{
let uploadTask = pictureStorageRef.putData(lowResImageData!,metadata: nil)
{metadata,error in
if (error == nil) {
let downloadUrl = storageRef.downloadURL { (url, error) in
guard let userProfile = UserService.currentUserProfile else { return }
let childUpdates = [
"author": [
"uid": userProfile.uid,
"username": userProfile.fullname,
"photoURL": userProfile.photoURL.absoluteString
],
"text": self.textView.text!,
"picture": downloadUrl!.absoluteString,
"timestamp": ["sv.":"timestamp"]
] as [String:Any]
}
}
}
}
}
`
Error is happening on this part: "picture": downloadUrl!.absoluteString,
As i can see yo got an "Variable used within its own initial value" error, that means you try to access initial value of variable within this variable, its not a good behavior.
I also see you dont use any data from your completion block, so try to use nil instead of your completion block and then try to access your downloadURL variable
Your updated code:
func postWithImageAndText() {
var imagesArray = [AnyObject]()
let postLength = textView.text.count
let numImages = imagesArray.count
let postRef = Database.database().reference().child("posts").childByAutoId()
let storageRef = Storage.storage().reference()
let uid = Auth.auth().currentUser
let pictureStorageRef = storageRef.child("users/profile/\(uid)/media")
if let lowResImageData = (imagesArray[0] as! UIImage).jpegData(compressionQuality: 0.50) {
if(postLength>0 && numImages>0)
{
pictureStorageRef.putData(lowResImageData, metadata: metaData) { (metadata, error) in
if error != nil {
print("Failed to upload image:", error as Any)
}
pictureStorageRef.downloadURL(completion: { (url, error) in
if error != nil {
print(error!)
}
if let imageUrl = url?.absoluteString {
guard let userProfile = UserService.currentUserProfile else { return }
let childUpdates = [
"author": [
"uid": userProfile.uid,
"username": userProfile.fullname,
"photoURL": userProfile.photoURL.absoluteString
],
"text": self.textView.text!,
"picture": imageUrl,
"timestamp": ["sv.":"timestamp"]
] as [String:Any]
}
})
}
}
}
}

UserInfo={NSLocalizedDescription=The email address is already in use by another account., error_name=ERROR_EMAIL_ALREADY_IN_USE}

Hey guys actually i am trying two things here:- trying to create a new account and trying to open a screen like which appears after login but it is showing "email already exist error".
#IBAction func CreateAcccountButton(_ sender: AnyObject) {
guard let eventInterest = textBox.text,let email = EmailTestfield.text, let password = PasswordTestfield.text, let name = UsernameTestfield.text else {
print("Form is not valid")
return
}
Auth.auth().createUser(withEmail: email, password: password, completion: { (user, error) in
if let error = error {
print(error)
return
}
guard let uid = user?.uid else {
return
}
//successfully authenticated user
let imageName = UUID().uuidString
let storageRef = Storage.storage().reference().child("profile_images").child("\(imageName).png")
if let uploadData = UIImagePNGRepresentation(self.Profilepicture.image!) {
storageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
if let error = error {
print(error)
return
}
print (metadata)
// let downloadURL = metadata?.downloadURL()
// print("URL ", downloadURL)
if let Profilepictureurl = metadata?.downloadURL()?.absoluteString {
let values = ["name": name, "email": email,"EventInterest":eventInterest,"Password":password,"Profilepictureurl": Profilepictureurl ]
let user = User(dictionary: values as [String : AnyObject])
let customViewController = MessagesController()
customViewController.setupNavBarWithUser(user)
customViewController.fetchUserAndSetupNavBarTitle()
// customViewController.navigationItem.title = values["name"] as? String
self.dismiss(animated: true, completion: nil)
self.registeruserintoDb(uid,values: values as [String : AnyObject])
}
})
}
}
)
}
fileprivate func registeruserintoDb(_ uid: String, values: [String: AnyObject]) {
let ref = Database.database().reference()
let usersReference = ref.child("users").child(uid)
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
if err != nil {
print(err!)
return
}
})
}
It's exactly what the error says, you already have a user with that email. Instead, use the auth.signIn method and check for currently signed in users.