Completion not working with Firebase Database - swift

The user can upload a profile picture and some information about himself in my app.
I want to write the url of the uploaded picture in firebase realtime database but it takes the placeholder text "testentry" and not the real url. Why does my completion not work here?
var imagePicker: UIImagePickerController!
var urltoPicture = "testentry"
#IBAction func updateProfile(_ sender: UIButton) {
uploadPic(arg: true, completion: { (success) -> Void in
if success {
linkUbertragen()
} else {
}
})
func uploadPic(arg: Bool, completion: #escaping (Bool) -> ()) {
guard let imageSelected = self.image else {
//print("ok")
return
}
guard let imageData = imageSelected.jpegData(compressionQuality: 0.1) else {
return
}
let storageRef = Storage.storage().reference(forURL: "gs://h......com")
let storageProfileRef = storageRef.child("profilePictures").child(Auth.auth().currentUser!.uid)
let metadata = StorageMetadata()
metadata.contentType = "image/jpg"
storageProfileRef.putData(imageData, metadata: metadata, completion: {
(storageMetadata, error) in
if error != nil {
//print(error?.localizedDescription)
return
}
storageProfileRef.downloadURL(completion: { (url, error) in
if let metaImageURL = url?.absoluteString {
print(metaImageURL)
self.urltoPicture = metaImageURL
}
})
})
completion(arg)
}
func linkUbertragen(){
ref = Database.database().reference()
let userID = Auth.auth().currentUser!.uid
ref.child("user/\(userID)").updateChildValues(["profileText": profileText.text!])
print(urltoPicture)
ref.child("user/\(userID)").updateChildValues(["picture": urltoPicture])
}
self.navigationController?.popViewController(animated: true)
}

This is a very common mistake. You have to call completion inside the (final) closure.
And it is good practice to call completion(false) always in case of an error – even better to return and handle all errors
func uploadPic(arg: Bool, completion: #escaping (Bool) -> ()) {
guard let imageSelected = self.image else {
//print("ok")
completion(false); return
}
guard let imageData = imageSelected.jpegData(compressionQuality: 0.1) else {
completion(false); return
}
let storageRef = Storage.storage().reference(forURL: "gs://h......com")
let storageProfileRef = storageRef.child("profilePictures").child(Auth.auth().currentUser!.uid)
let metadata = StorageMetadata()
metadata.contentType = "image/jpg"
storageProfileRef.putData(imageData, metadata: metadata, completion: {
(storageMetadata, error) in
if error != nil {
//print(error?.localizedDescription)
completion(false); return
}
storageProfileRef.downloadURL(completion: { (url, error) in
if let metaImageURL = url?.absoluteString {
print(metaImageURL)
self.urltoPicture = metaImageURL
completion(true)
} else {
completion(false)
}
})
})
}
The arg parameter is actually not needed.

Related

"The file "..." couldn’t be opened because there is no such file" uploading video from phpickerviewcontroller to FirebaseStorageq

I'm trying to upload a video file using phpickerviewcontroller, but I'm running into an issue uploading the URL to FirebaseStorage. Here is some code:
func uploadVideo(videoURL: URL)
{
let storage = Storage.storage()
let storageRef = storage.reference()
let videoRef = storageRef.child("rPosts/\(uid!)/\(fileID)")
let metadata = StorageMetadata()
metadata.contentType = "video/quicktime"
var videoData: Data = Data()
do
{
videoData = try Data(contentsOf: videoURL)
}
catch
{
print(error.localizedDescription)
return
}
videoRef.putData(videoData, metadata: metadata)
{ (metaData, error) in
guard error == nil else
{
self.errorLabel.text = error!.localizedDescription
return
}
}
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult])
{
dismiss(animated: true, completion: nil)
guard let itemProvider = results.first?.itemProvider else { return }
itemProvider.loadItem(forTypeIdentifier: "com.apple.quicktime-movie", options: nil)
{ (videoFile, error) in
guard error == nil else { return }
let videoFile = videoFile as? URL
DispatchQueue.main.async
{
self.uploadVideo(videoURL: videoFile!)
print(videoFile!)
}
self.uploadedYet = true
}
}
I've tried using .putFile but it keeps on saying
Ensure file URL is not a directory, symbolic link, or invalid url.
When I use .putData it says
The file "..." couldn’t be opened because there is no such file
EDIT:
itemProvider.loadFileRepresentation(forTypeIdentifier: "com.apple.quicktime-movie")
{ (videoURL, error) in
guard error == nil else { return }
print("isbeingcalled") //does not get calleed :(
DispatchQueue.main.async
{
let storageRef = Storage.storage().reference()
let videoRef = storageRef.child("rPosts/\(self.uid!)/\(self.fileID).mov")
let metadata = StorageMetadata()
metadata.contentType = "video/quicktime"
print("run")
videoRef.putFile(from: videoURL!, metadata: metadata)
{ (metaData, error) in
guard error == nil else
{
print(videoURL!)
print(videoRef.fullPath)
self.errorLabel.text = error!.localizedDescription
print(error!.localizedDescription)
return
}
}
}
self.uploadedYet = true
}

For loop not accounting for all items?

I have a multi-selection imagepicker with the intention of allowing the user to select multiple assets, then upload each asset to the database. In the completion handler, I take all the selected assets and pass them to a custom function: uploadImageAssets(assets: [PHAsset], projectRef: DocumentReference), where the upload begins.
In the function, I'm using a for-loop to upload each asset individually. While the assets are being uploaded correctly, not all assets are being uploaded. Lets say I've selected 5 assets... Only 4 will show up in the database, and they'll all be the same image, repeated. Any idea as to why this is happening? Here is my code below:
Image Picker Selection:
#IBAction func uploadProjectTapped(_ sender: Any) {
let imagePicker = ImagePickerController()
imagePicker.settings.selection.max = 10
imagePicker.settings.theme.selectionStyle = .numbered
imagePicker.settings.fetch.assets.supportedMediaTypes = [.image, .video]
imagePicker.settings.selection.unselectOnReachingMax = false
let start = Date()
self.presentImagePicker(imagePicker, select: { (asset) in
print("Selected: \(asset)")
}, deselect: { (asset) in
print("Deselected: \(asset)")
}, cancel: { (assets) in
print("Canceled with selections: \(assets)")
}, finish: { (assets) in
print("Finished with selections: \(assets)")
self.getAssetThumbnail(assets: assets)
}, completion: {
let finish = Date()
print(finish.timeIntervalSince(start))
})
}
And, the function to add them to Firestore:
func uploadImageAsset(assets: [PHAsset], projectRef: DocumentReference) {
let userID = Auth.auth().currentUser?.uid
let db = Firestore.firestore()
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
option.isSynchronous = false
option.isNetworkAccessAllowed = true
option.resizeMode = .exact
option.version = .original
option.deliveryMode = .highQualityFormat
let uniqueImageID = NSUUID().uuidString
let storageRef = Storage.storage().reference().child("project-images").child("\(uniqueImageID).jpeg")
for asset in assets {
let imageSize = CGSize(width: asset.pixelWidth, height: asset.pixelHeight)
manager.requestImage(for: asset, targetSize: imageSize, contentMode: .aspectFill, options: option) { (image, info) in
let uploadData = image?.jpegData(compressionQuality: 0.6)
storageRef.putData(uploadData!, metadata: nil, completion: {
(metadata, error) in
if error != nil {
return
} else {
storageRef.getMetadata(completion: { (metadata, err) in
if let error = err {
print(error)
} else {
storageRef.downloadURL(completion: { (url, err) in
if let error = err {
print(error)
} else {
self.imageAssetURLs.append((url?.absoluteString)!)
guard let url = url?.absoluteString else { return }
projectRef.updateData(["images": FieldValue.arrayUnion([url])], completion: { (err) in
if err != nil {
print(err)
} else {
self.dismiss(animated: true, completion: nil)
}
})
}
})
}
})
}
})
}
}
}
I have a strong feeling that the error lies within this line:
self.imageAssetURLs.append((url?.absoluteString)!)
guard let url = url?.absoluteString else { return }
in func uploadImageAsset(...) the
let uniqueImageID = NSUUID().uuidString
and
let storageRef = Storage.storage().reference().child("project-images").child("\(uniqueImageID).jpeg")
should be inside the loop just before
storageRef.putData(..)

using the image assigned to userprofile in firebase and attachine it to profile page

I have been looking online everywhere to help me out here but I cannot figure this out. The code below says that allows for the image to get stored in the firebase database when the user registers:
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)
}
}
}
func saveProfile(name: String, email: String, profileImageURL: URL, completion: #escaping ((_ success:Bool)->())){
guard let uid = Auth.auth().currentUser?.uid else {return}
let databaseRef = Database.database().reference().child("users/profile/\(uid)")
let userObject = ["name":name, "email":email,
"photoURL":profileImageURL.absoluteString] as [String:Any]
databaseRef.setValue(userObject) {error, ref in completion(error == nil)}
}
}
This code in a different .swift file allows me to get the photo:
static let cache = NSCache<NSString, UIImage>()
static func downloadImage(withURL url:URL, completion: #escaping (_ image:UIImage?, _ url:URL)->()) {
let dataTask = URLSession.shared.dataTask(with: url) { data, responseURL, error in
var downloadedImage:UIImage?
if let data = data {
downloadedImage = UIImage(data: data)
}
if downloadedImage != nil {
cache.setObject(downloadedImage!, forKey: url.absoluteString as NSString)
}
DispatchQueue.main.async {
completion(downloadedImage, url)
}
}
dataTask.resume()
}
static func getImage(withURL url:URL, completion: #escaping (_ image:UIImage?, _ url:URL)->()) {
if let image = cache.object(forKey: url.absoluteString as NSString) {
completion(image, url)
} else {
downloadImage(withURL: url, completion: completion)
}
}
user profile code:
class UserProfile {
var uid:String
var name:String
var photoURL:URL
init(uid:String, name:String, photoURL:URL){
self.uid = uid
self.name = name
self.photoURL = photoURL
}
}
I have added a file that will allow me to get the data from firebase:
class ImageServices{
static let cache = NSCache<NSString, UIImage>()
static func downloadImage(withURL url:URL, completion: #escaping (_ image:UIImage?, _ url:URL)->()) {
let dataTask = URLSession.shared.dataTask(with: url) { data, responseURL, error in
var downloadedImage:UIImage?
if let data = data {
downloadedImage = UIImage(data: data)
}
if downloadedImage != nil {
cache.setObject(downloadedImage!, forKey: url.absoluteString as NSString)
}
DispatchQueue.main.async {
completion(downloadedImage, url)
}
}
dataTask.resume()
}
static func getImage(withURL url:URL, completion: #escaping (_ image:UIImage?, _ url:URL)->()) {
if let image = cache.object(forKey: url.absoluteString as NSString) {
completion(image, url)
} else {
downloadImage(withURL: url, completion: completion)
}
}
}
Now how do I use this in my viewController to actually show the image on the imageView???
The last edit to the question asks this
Now how do I use this in my viewController to actually show the image
on the imageView???
This indicates you have a viewController that contains an imageView as you're asking how to assign the downloaded image to that imageView.
The image is downloaded here in your code
static func downloadImage(withURL...
var downloadedImage:UIImage
if let data = data {
downloadedImage = UIImage(data: data)
so a single line of code following that will take the downloadedImage and display it in the imageView
self.myImageView.image = downloadedImage

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.

metadata?.downloadURL()?.absoluteString

I have a some problem with old syntax like:
metadata?.downloadURL()?.absoluteString"
How to use new way in my code?
Error in this part code:
let downloadURL = metadata?.storageReference?.downloadURLWithCompletion()
Full code:
Storage.storage().reference().child(imgUid).putData(imgData, metadata: metadata) { (metadata, error) in
if error != nil {
print("Did'n upload image")
} else {
print("uploaded")
let downloadURL = metadata?.storageReference?.downloadURLWithCompletion()
if let url = downloadURL {
self.setUser(img: url)
}
}
}
This is a easyway to do this use this Func to save the data to the FirebaseStorage then take the url as a String then you can save it on
func uploadImageToFirebaseStorage(data: Data, onSuccess: #escaping (_ imageUrl: String) -> Void) {
let photoIdString = NSUUID().uuidString
let storageRef = Storage.storage().reference(forURL: Config.STORAGE_REF_URL).child(POST_REF).child(photoIdString)
storageRef.putData(data, metadata: nil) { (metadata, error) in
if let error = error {
debugPrint(error.localizedDescription)
return
}
metadata?.storageReference?.downloadURL(completion: { (url, error) in
if let error = error {
print(error.localizedDescription)
return
}
onSuccess("\(url!)")
})
}
}
func save() {
let newPostRef = Database.database().reference().child("users").childByAutoId()
let newPostKey = newPostRef.key
// 1. save image
if let imageData = self.profilPic!.jpegData(compressionQuality:0.5) {
let storage = Storage.storage().reference().child("profileImages")
DispatchQueue.main.sync {
storage.putData(imageData).observe(.success, handler: { (snapshot) in
storage.downloadURL(completion: { (url, error) in
if error != nil {
print(error!.localizedDescription)
return
}
if let profileImageUrl = url?.absoluteString {
guard let uid = (Auth.auth().currentUser?.uid) else {return}
let values = ["name": self.name, "email": self.email, "profilePictureUrl": profileImageUrl]
newPostRef.setValue(values)
self.ref.child(uid).childByAutoId().setValue(values)
}
})
})
}
}
}