Firebase Upload URL to real time database - swift

I'm trying to upload image url from Firebase Storage to Realtime database.
Here's the code
#IBOutlet weak var jobTitle: UITextField!
#IBOutlet weak var companyName: UITextField!
#IBOutlet weak var jobLocation: UITextField!
#IBOutlet weak var ImageView1stPoster: UIImageView!
var imageUploaded = Data()
var URLtoRealtime = ""
func addPost() {
ref.child("Poster").childByAutoId().setValue(["jobName": jobTitle.text as Any,
"companyTitle": companyName.text as Any,
"jobLocation": jobLocation.text as Any,
"firstPoster": URLtoRealtime as Any,
/*,
"timeStamp":[".sv":"timestamp"]*/]
as[String:Any])
}
// DoneButton to submit everthing :)
#IBAction func DoneButton(_ sender: Any) {
uploadImageToFirebase(imageData: imageUploaded)
createAlert(title: "Post has been submitted", message: "Going to home page")
addPost()
}
func uploadImageToFirebase(imageData: Data) {
// References and vars
let StorageRefrenece = Storage.storage().reference()
let currentUser = Auth.auth().currentUser
let posterImageRef = StorageRefrenece.child("posters").child(currentUser!.uid).child("posterOne.jpg")
let uploadMetaData = StorageMetadata()
uploadMetaData.contentType = "image/jpeg"
// putData to put data to the server using MetaData to orignize everthing.
posterImageRef.putData(imageData, metadata: uploadMetaData) { (uploadedImageMeta, error) in
if error != nil {
print("Error Took place \(String(describing: error?.localizedDescription))")
return
} else {
print("metaData of uploaded image \(uploadMetaData)")
}
}
posterImageRef.downloadURL { (url, error) in
if (error != nil) {
// Handle any errors
print(error!.localizedDescription)
print("NOOOPPPEEE")
} else {
// Get the download URL for 'images/stars.jpg'
print("Working Good")
let UrlString = url!.absoluteString
print(UrlString)
self.URLtoRealtime = UrlString
}
}
}
I'm trying like to make URLString = URLtoRealtime data and then
add the post to firebase.
but what is happening is that it executes addPost() function before
self.URLtoRealtime = UrlString
I don't know how to make the program to execute the previous line of code before addpost() function.

When the done button is touched, you're asynchronously uploading the image to firebase and downloading the URL. However, as you've pointed out, by the time you addPost, the URL hasn't been downloaded yet – you want to do one after the other.
#IBAction func DoneButton(_ sender: Any) {
uploadImageToFirebase(imageData: imageUploaded) { [weak self] (url, error) in
if let url = url {
createAlert(title: "Post has been submitted", message: "Going to home page")
self?.URLtoRealtime = url.absoluteString
self?.addPost()
} else {
self?.createAlert(title: "Post could not be submitted", message: "Try again")
}
}
}
We can add a completion argument to the upload method so that you can addPost once the upload and download of the URL is finished.
func uploadImageToFirebase(imageData: Data, completion: #escaping (URL?, Error?) -> ()) {
guard let uid = Auth.auth().currentUser?.uid else { return completion(nil, nil) }
let posterImageRef = Storage.storage().reference(withChild: "posters/\(uid)/posterOne.jpg")
let uploadMetaData = StorageMetadata(dictionary: [ "contentType": "image/jpeg" ])
posterImageRef.putData(imageData, metadata: uploadMetaData) { (metadata, error) in
if let ref = metadata?.storageReference {
ref.downloadURL(completion: { (url, error) in
completion(url, error)
})
} else {
completion(nil, error)
}
}
}

Related

Why do I have Problem with upload pic in swift. Problem with nil unwrap. URL

** Update**
My point is im trying to match my profilePicLink with image I upload from my library.
Also selectedUsers is Users Type as following
var username : String = ""
var email : String = ""
var uid : String = ""
var profilePicLink : String = ""
init(username : String, email: String, uid : String, profilePicLink: String ) {
self.username = username
self.email = email
self.uid = uid
self.profilePicLink = profilePicLink
}
I am having problem when I am trying to upload photo. The action are
I pick the photo from my library
#IBAction func getPhotoButton(_ sender: Any) {
let image = UIImagePickerController()
image.delegate = self
image.sourceType = UIImagePickerController.SourceType.photoLibrary
self.present(image, animated: true, completion: nil)
}
It leads me to my photo library. After I pick my photo. I click on button "Update" with the action as following code
#IBAction func updatePhoto(_ sender: Any) {
uploadPhoto()
}
func uploadPhoto(){
selectedUser?.uploadProfileImage(imageView.image!){
url in print (URL.self)
}
}
I got the error as ** Fatal error: Unexpectedly found nil while unwrapping an Optional value: ** in the func uploadPhoto as the picture
Fatal Error
And here is the code of func in my other class (Users) for upload and get Profile Image
func getProfileImage() -> UIImage {
if let url = NSURL(string: profilePicLink){
if let data = NSData(contentsOf: url as URL) {
return UIImage(data: data as Data)!
}
}
return UIImage()
}
func uploadProfileImage(_ image:UIImage, completion: #escaping ((_ url:URL?)->())) {
guard let uid = Auth.auth().currentUser?.uid else { return }
let storageRef = Storage.storage().reference().child("user/\(uid)")
guard let imageData = image.jpegData(compressionQuality: 0.75) else { return }
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
storageRef.putData(imageData, metadata: metaData) { metaData, error in
if error == nil, metaData != nil {
storageRef.downloadURL { url, error in
completion(url)
// success!
}
} else {
// failed
completion(nil)
}
}
}
Updated : I modifed my function uploadProfileImage as following. My point is I wanna assign profilePicLink variables to the downloadurl. And then I update value of profilePicLink
func uploadProfileImage(_ image:UIImage, completion: #escaping ((_ url:URL?)->())) {
let storageRef = Storage.storage().reference().child("profileImages").child("\(NSUUID().uuidString).jpg")
guard let imageData = image.jpegData(compressionQuality: 0.75) else { return }
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
storageRef.putData(imageData, metadata:metaData) { (metaData, error) in
if error != nil, metaData != nil {
storageRef.downloadURL (completion: {(url, error) in
if error != nil {
if let downloadurl = url?.absoluteString {
if (self.profilePicLink == "") {
self.profilePicLink = downloadurl
Database.database().reference().child("users").child(self.uid).updateChildValues(["profilePicLink":downloadurl])
}
}
} else {
completion(nil)
}
}
)
}
}
}
Please be advised on this.

How to perform segue ONLY if token not nil swift 5

As the title is saying, im working on a login. The tokenHandler is already working and im using a KeychainAccess.
Here my tokenHandler class:
import KeychainAccess
class TokenHandler {
func saveUsernameToKeyChain(username: String) {
do {
try keychain.set(username, key: "myUsername")
} catch let error {
print(error)
}
}
func getUsernameFromKeyChain() -> String? {
return keychain[string: "myUsername" ]
}
func saveUserPasswordToKeyChain(password: String) {
do {
try keychain.set(password, key: "UserPassword")
} catch let error {
print(error)
}
}
func getUserPasswordFromKeyChain() -> String? {
return keychain[string: "UserPassword"]
}
let keychain = Keychain(service: "com.mybackendpage")
func getTokenFromKeyChain() -> String? {
return keychain[string: "myToken"]
}
func saveTokenToKeyChain(token: String) {
do {
try keychain.set(token, key: "myToken")
}
catch let error {
print(error)
}
}
func saveRefreshTokenToKeyChain(refreshToken: String) {
do {
try keychain.set(refreshToken, key: "myRefreshToken")
}
catch let error {
print(error)
}
}
func loginToAPI(username: String, password: String) -> Any {
guard let url = URL(string: "https:mypage.com") else
{
return ""
}
let jsonData = try? JSONSerialization.data(withJSONObject: [
"email": username,
"password": password
])
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// insert json data to the request
request.httpBody = jsonData
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else { print(error!.localizedDescription); return }
guard let data = data else { print("Empty data"); return }
if let str = String(data: data, encoding: .utf8) {
print(str)
}
}.resume()
return "TOKENSTRING"
}
}
And here my LoginVC class:
class LoginViewController: UIViewController {
let tokenHandler = TokenHandler()
#IBOutlet weak var usernameTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
let username = tokenHandler.getUsernameFromKeyChain()
let userPassword = tokenHandler.getUserPasswordFromKeyChain()
#IBAction func unwindToLogin(_ unwindSegue: UIStoryboardSegue) {
print("-unwinding success-")
}
// func for the login button:
#IBAction func loginButton(_ sender: UIButton) {
activityIndicator.startAnimating()
loginWithCredentials()
let token = tokenHandler.getTokenFromKeyChain()
if token != nil {
performSegue(withIdentifier: "segueToNavigation", sender: self)
} else if ( token == nil ){
// create the alert:
let alert = UIAlertController(title: "Wrong login data", message: "Please try again.", preferredStyle: UIAlertController.Style.alert)
// add an action to the button:
alert.addAction(UIAlertAction( title: "Ok", style: UIAlertAction.Style.default, handler: nil ))
// show the alert:
self.presentingViewController
print("-Token could not be created.-")
}
else {
// create the alert:
let alert = UIAlertController(title: "Wrong login data", message: "Please try again.", preferredStyle: UIAlertController.Style.alert)
// add an action to the button:
alert.addAction(UIAlertAction( title: "Ok", style: UIAlertAction.Style.default, handler: nil ))
// show the alert:
self.presentingViewController
print("-Token could not be created.-")
}
}
func loginWithCredentials() {
let username: String = usernameTextField.text!
let password: String = passwordTextField.text!
let authResponse = tokenHandler.loginToAPI(username: username, password: password)
}
}
Im still not skilled swift programmer, so I will be happy if any of you could give me some good advices. I was reading and trying to work with the delegate principle, but frankly, my guts are telling me, that this is not what I need.
I was reading about
PerformSegueWithIdentifier
but not really understand how to transform it into my code...
The segues which I have included storyboardwise are working, but unfortunately also, if the test user didn't do the login. So, im pressing the login button w/o any username and userpwd and im getting anyway to the next View. Not cool, so help me please :)
EDIT: I changed performSegue to shouldPerformSegue but im still getting access to the next View w/o any permission.
EDIT: Im getting:
-Token could not be created.-
{"message":"The given data was invalid.","errors":{"email":["The email field is required."],"password":["The password field is required."]}}
So the error is correct, but by pressing on the "Login" Button im still getting to the next View.
EDIT:
Ive tried a few changes, now I have for eg:
if tokenHandler.getTokenFromKeyChain() != nil
instead of
let token = tokenHandler.getTokenFromKeyChain()
if token != nil
Apparently, nothing what im doing in this IBAction for the LoginButton does anything different. What am I missing?
Well, it looks to me, that if you did once call saveTokenToKeyChain() ever since you've been running the app on your device/simulator, then the KeyChain will hold some string there, as I can't see where you set it to nil (I can't see where you set it at all, but let's suppose that you deleted the code that saves the token). So what your current logic does is that it performs the segue if you have some string saved as token (no matter if it's empty string, random string or an expired token). Whatever was left there, getTokenFromKeyChain() will return you a string so your if token != nil will always evaluate to true.
On the other hand, if you clear the KeyChain data, as I can't find any piece of code that saves the token, the UI will always say that the login fails, even if it actually succeeds.
So you should handle the login success/failure with properly writing/deleting the token to/from KeyChain.

Upload an image from device to firebase

I have been trying to use the code that Firebase documents provide and this is how much I have developed it. It just doesn't work. I have tried putting Storage.storage().reference() but it brings in more error to my code.
let downloadURL: String!
if let imageData = selectedImage.jpegData(compressionQuality: 0.2){
let imgUid = NSUUID().uuidString
let uploadTask = storage.reference().putData(imageData, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
return
}
downloadURL = metadata.downloadURL
The error I receive is use of unidentified resolver storage. But when I try Storage.storage().reference() it brings in 10 different error throughout my code.
Well, I don't know whats going on but, this works for me,
Make sure your pods has at least these in there.
PODFILE
pod 'Firebase/Storage'
pod 'Firebase/Auth' #Auth isn't needed but, you should really use it.
View controller
import UIKit
import FirebaseStorage
class TestView: UIViewController {
var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView = UIImageView()
}
#IBAction func uploadPicture(_ sender: Any) {
self.imageView.image = #imageLiteral(resourceName: "playlist.png")
uploadMedia() { url in
guard let url = url else { return }
print(url)
}
}
func uploadMedia(completion: #escaping (_ url: String?) -> Void) {
let storageRef = Storage.storage().reference().child("something.png")
if let uploadData = self.imageView.image?.pngData(){ //You can change this to jpeg, etc
storageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print("error")
completion(nil)
} else {
storageRef.downloadURL(completion: { (url, error) in
print(url?.absoluteString)
completion(url?.absoluteString)
})
}
}}}
}

Issue with Firebase Storage & Database

I'm having an app, with UpdateProfileViewController (iOS app, written in Swift). I'm using Firebase storage and Database as backend. The problem that I'm facing is that I have 2 images that need to be uploaded to the Firebase storage, then get the download URL and enter it in the database for that particular user. Now, I'm facing 3 problems.
The downloadURL method should return a URL pointing to the uploaded image in the storage and assign it to the variable storageHeaderDownloadedURL and the other one is headerImgDownloadedURL (those should be the header and the profile images).
I want to update all of the fields despite of the fact if the user changed something or not, just update the entire user profile.
I would like to be able to compress the the images and make them smaller in terms of size, not quality, so the users don't upload very big images.
Here's my entire code, I hope someone could help me out with that because I spent 2 days and I still cannot figure those things out:
#IBAction func savePressed(_ sender: UIBarButtonItem)
{
updateUserProfile()
}
func updateUserProfile ()
{
if let userID = FIRAuth.auth()?.currentUser?.uid
{
// Note: Storage references to profile images & profile headers folder
let storageUserProfileID = Storage.storage.profile_images.child(userID)
let storageUserHeaderID = Storage.storage.profile_headers.child(userID)
guard let imageProfile = profileImage.image else { return }
guard let headerImage = headerImage.image else { return }
var storageProfileDownloadedURL: String = ""
var storageHeaderDownloadedURL: String = ""
if let newProfileImage = UIImagePNGRepresentation(imageProfile), let newHeaderImage = UIImagePNGRepresentation(headerImage)
{
storageUserProfileID.put(newProfileImage, metadata: nil, completion: { (metadata, error) in
if error != nil
{
showAlert(title: "Oops!", msg: (error?.localizedDescription)!, actionButton: "OK", viewController: self)
return
}
// Get the URL from the storage
storageUserProfileID.downloadURL(completion: { (url, error) in
if error != nil
{
showAlert(title: "Oops!!!", msg: (error?.localizedDescription)!, actionButton: "OK", viewController: self)
return
}
if let profileImgDownloadedURL = url?.absoluteString
{
storageProfileDownloadedURL = profileImgDownloadedURL
}
})
})
storageUserHeaderID.put(newHeaderImage, metadata: nil, completion: { (metadata, error) in
if error != nil
{
showAlert(title: "Oops!", msg: (error?.localizedDescription)!, actionButton: "OK", viewController: self)
return
}
// Get the URL from the storage
storageUserHeaderID.downloadURL(completion: { (url, error) in
if error != nil
{
showAlert(title: "Oops!!!", msg: (error?.localizedDescription)!, actionButton: "OK", viewController: self)
return
}
else
{
if let headerImgDownloadedURL = url?.absoluteString
{
storageHeaderDownloadedURL = headerImgDownloadedURL
}
}
})
})
//Note: Update the info for that user in Database
}
guard let newDisplayName = self.displayNameTextField.text else { return }
guard let newLocation = self.locationTextField.text else { return }
guard let newDescription = self.bioTextField.text else { return }
guard let newWebsite = self.websiteTextField.text else { return }
guard let newBirthday = self.birthdayTextField.text else { return }
let newUpdatedUserDictionary = ["imageProfile": storageProfileDownloadedURL,
"imageHeader" : storageHeaderDownloadedURL,
"description" : newDescription,
"location": newLocation,
"displayName": newDisplayName,
"website": newWebsite,
"birthday": newBirthday,
]
Database.dataService.updateUserProfile(uid: userID, user: newUpdatedUserDictionary)
print("Successfully updated!")
showAlert(title: "Profile updated", msg: "YAASS", actionButton: "OK", viewController: self)
}
}

Uploading Image to Firebase Storage and Database

I want to put the download URL of images into my Firebase Database. I can upload the Image into storage but I can't figure out how to get the URL into my database with the rest of the "post".
#IBOutlet weak var titleText: UITextField!
#IBOutlet weak var authorText: UITextField!
#IBOutlet weak var mainText: UITextView!
#IBOutlet weak var dateText: UITextField!
#IBOutlet weak var myImageView: UIImageView!
var ref:FIRDatabaseReference?
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func uploadImage(_ sender: Any) {
let image = UIImagePickerController()
image.delegate = self
image.sourceType = UIImagePickerControllerSourceType.photoLibrary
image.allowsEditing = false
self.present(image, animated: true)
{
//after its completed
}
}
#objc(imagePickerController:didFinishPickingMediaWithInfo:) func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage
{
myImageView.image = image
}
else
{
//error
}
self.dismiss(animated: true, completion: nil)
let storageRef = FIRStorage.storage().reference().child("myImage.png")
if let uploadData = UIImagePNGRepresentation(self.myImageView.image!){
storageRef.put(uploadData, metadata: nil, completion:
{
(metadata, error) in
if error != nil {
print("error")
return
}
print(metadata)
//how do I put the download URL in the metadata into my database
}
)
}
}
#IBAction func addPost(_ sender: Any) {
if self.titleText.text != "" && self.authorText.text != "" && self.mainText.text != "" && self.dateText.text != ""
{
ref?.child("Posts").childByAutoId().setValue(["Title": titleText.text,"Article": mainText.text, "Author": authorText.text, "Date": dateText.text, "myImageURL": myImageURL])
//the myImageURL part is where I get an error
self.performSegue(withIdentifier: "post", sender: self)
}
else{
let alertController = UIAlertController(title: "Oops!", message: "Field left blank", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "Ok", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}
}
Organize your upload and save funcs like this:
func uploadMedia(completion: #escaping (_ url: String?) -> Void) {
let storageRef = FIRStorage.storage().reference().child("myImage.png")
if let uploadData = UIImagePNGRepresentation(self.myImageView.image!) {
storageRef.put(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print("error")
completion(nil)
} else {
completion((metadata?.downloadURL()?.absoluteString)!))
// your uploaded photo url.
}
}
}
Next just connect to FIRDatabase and save it to your node.
#IBAction func addPost(_ sender: Any) {
if self.titleText.text != "" && self.authorText.text != ""
&& self.mainText.text != "" && self.dateText.text != "" {
uploadMedia() { url in
guard let url = url else { return }
ref?.child("Posts").childByAutoId().setValue([
"Title" : titleText.text,
"Article" : mainText.text,
"Author" : authorText.text,
"Date" : dateText.text,
"myImageURL" : url
])
}
}
You can also look at my answer about uploading data and saving URL's to database
For Updated Firebase Version And Swift 4.2 Code :
func uploadMedia(completion: #escaping (_ url: String?) -> Void) {
let storageRef = Storage.storage().reference().child("\(Auth.auth().currentUser?.uid ?? "").png")
if let uploadData = self.imgUploadView.image?.jpegData(compressionQuality: 0.5) {
storageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print("error")
completion(nil)
} else {
storageRef.downloadURL(completion: { (url, error) in
print(url?.absoluteString)
completion(url?.absoluteString)
})
// completion((metadata?.downloadURL()?.absoluteString)!))
// your uploaded photo url.
}
}
}
}
100% working tested just one add function
//MARK: - Upload image
func uploadImage(_ image: UIImage){
let imageName:String = String("\(CurrentTimeStampInSecond).png")
let storageRef = Storage.storage().reference().child("profilePic").child(imageName)
let compressImage = HelperFunction.helper.resizeImage(image: image)
if let uploadData = UIImagePNGRepresentation(compressImage){
storageRef.putData(uploadData, metadata: nil
, completion: { (metadata, error) in
if error != nil {
print("error")
self.stopAnimating()
showAlertWithTitleWithMessage(message: "Please try again later")
return
}else{
self.stopAnimating()
}
let strPic:String = (metadata?.downloadURL()?.absoluteString)!
print(metadata)
//self.imagePath = (metadata?.downloadURL()?.absoluteString)!
//self.sendMessageOnServer()
print("\n\n\n\n\n\n ===download url : \(strPic)")
})
}
}
It might be too late, but I have found an easier way by using a Utility Class to Upload Images & Files to Firebase Storage. You can upload images and files with a single method call, using the above utility class. like
if let data = image.pngData() { // convert your UIImage into Data object using png representation
FirebaseStorageManager().uploadImageData(data: data, serverFileName: "your_server_file_name.png") { (isSuccess, url) in
print("uploadImageData: \(isSuccess), \(url)")
}
}
Use UIImagePNGRepresentation in swift 4.2 like this,
if let uploadData = profileImageView.image?.pngData() {
storageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
}
}
}