Upload AVAsset to Firebase Storage - ios

I'm trying to upload a video that the app has as an AVAsset into Firebase Storage. The problem is that I am having problems both converting it to Data or just uploading as a file. As of now I am trying to export and upload its URL, but the app crasher without explanation (the dreaded (llbd)).
let uuid: String = UUID().uuidString
let imagesRef = storageRef.child("\(uuid)")
let exporter = AVAssetExportSession(asset: videos[i], presetName: AVAssetExportPresetHighestQuality)
let uploadTask = imagesRef.putFile(from: exporter!.outputURL!, metadata: nil) { (metadata, error) in //app crasher here
guard let metadata = metadata else {
return
}
let downloadURL = metadata.downloadURL()
//REST OF CODE
Any ideas how to make this process possible?

I don't know if it's still relevant but you have to use it a new way as:
imagesRef.downloadURL(completion: { (url, error) in //code })
Hope this helps

Related

Firebase Storage -File at URL: is not reachable. ensure file url is not a directory, symbolic link, or invalid url

I have a video in one location that I want to copy to another location but I keep getting an error
File at URL:
https://firebasestorage.googleapis.com/v0/b/myapp.appspot.com/o/...mp4?alt=media&token=...
is not reachable. Ensure file URL is not a directory, symbolic link,
or invalid url.
let strFromFirstLocation = "https://firebasestorage.googleapis.com/v0/b/myapp.appspot.com/o/...mp4?alt=media&token=..." // this url is alive
guard let url = URL(string: strFromFirstLocation) else { return }
let secondLocationRef = Storage.storage().reference().child("copy").child(postId)
secondLocationRef.putFile(from: url, metadata: nil, completion: { (metadata, error) in
if let error = error {
print(error.localizedDescription)
return
}
})
I had to do 2 things to fix the issue:
1- I had to convert the first firebase url to AVURLAsset > AVMutableComposition > AVAssetExportSession and I used the exporter.outputURL to save instead.
let strFromFirstLocation = "https://firebasestorage.googleapis.com/v0/b/myapp.appspot.com/o/...mp4?alt=media&token=..."
guard let url = URL(string: strFromFirstLocation) else { return }
let asset = AVURLAsset(url: url)
let mixComposition = AVMutableComposition()
// ...
guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
// ...
guard let exporterOutputURL = exporter.outputURL else { return }
2- I had to add .mp4 to the path as in "\(postId).mp4":
let secondLocationRef = Storage.storage().reference().child("copy").child("\(postId).mp4")
secondLocationRef.putFile(from: exporterOutputURL, metadata: nil, completion: { (metadata, error) in
if let error = error {
print(error.localizedDescription)
return
}
})

Can't Upload Video to Firebase Storage on iOS 13

Works perfectly fine on iOS 12.
Simple boilerplate code:
let storageRef = storage.reference().child("\(profile.studioCode)/\(selected.classId)/\(uploadDate)")
//Upload file and metadata
let uploadTask = storageRef.putFile(from: videoURL, metadata: metadata)
//Listen for state changes and, errors, and completion of the upload
uploadTask.observe(.resume) { (snapshot) in
//upload resumed or started
}
uploadTask.observe(.pause) { (snapshot) in
//upload paused
}
uploadTask.observe(.progress) { (snapshot) in
//upload progress
}
uploadTask.observe(.success) { (snapshot) in
//upload successful
}
uploadTask.observe(.failure) { (snapshot) in
//upload failed
}
Gives me:
Error Domain=FIRStorageErrorDomain Code=-13000 "An unknown error occurred, please check the server response."
I've updated Cocoapods and Firebase to the newest versions, tried allowing arbitrary loads, and tried signing out and back into the app to reset my auth token. In iOS 13 it throws that error immediately on upload, but on iOS 12 it uploads perfectly fine. Any help or insight would be greatly appreciated. Thanks!
I had a similar issue but here is an easy workaround: You need to use '.putData' instead of '.putFile' and specify the MIME type on upload.
let metadata = StorageMetadata()
//specify MIME type
metadata.contentType = "video/quicktime"
//convert video url to data
if let videoData = NSData(contentsOf: videoURL) as Data? {
//use 'putData' instead
let uploadTask = storageRef.putData(videoData, metadata: metadata)
}
How I ended up fixing it:
It turns out that file paths are different in iOS 13 than iOS 12:
iOS12 path:
file:///private/var/mobile/Containers/Data/Application/DF9C58AB-8DCE-401B-B0C9-2CCAC69DC0F9/tmp/12FD0C43-F9A0-4DCB-96C3-18ED83FED424.MOV
iOS13 path:
file:///private/var/mobile/Containers/Data/PluginKitPlugin/5DFD037B-AC84-463B-84BD-D0C1BEC00E4C/tmp/trim.7C8C6CD1-97E7-44D4-9552-431D90B525EA.MOV
Note the extra '.' in the iOS13 path. My solution was to, inside of my imagePickerController didFinishPickingMediaWithInfo function, copy the file into another temp directory, upload it from there, and then delete the copy.
do {
if #available(iOS 13, *) {
//If on iOS13 slice the URL to get the name of the file
let urlString = videoURL.relativeString
let urlSlices = urlString.split(separator: ".")
//Create a temp directory using the file name
let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let targetURL = tempDirectoryURL.appendingPathComponent(String(urlSlices[1])).appendingPathExtension(String(urlSlices[2]))
//Copy the video over
try FileManager.default.copyItem(at: videoURL, to: targetURL)
picker.dismiss(animated: true) {
self.videoRecorded = false
self.showUpload(targetURL)
}
}
else {
//If on iOS12 just use the original URL
picker.dismiss(animated: true) {
self.videoRecorded = false
self.showUpload(videoURL)
}
}
}
catch let error {
//Handle errors
}

videos uploaded to firebase are 0 seconds long?

I have an app where you can upload videos to firebase. The problem I have recently encountered is that all videos uploaded to firebase are 0 sec long (This is when in the DB, before, as in in the app, they are of correct length), which is of course wrong.
Some things I've tried:
I check how I upload which i also included bellow and it seems correct.
thumbnail is of type UIImage
The video right before I send it (in the preview I have in-app) the video looks perfect.
Another thing I noticed is that a thumbnail I upload with the video which is an image is being uploaded as a video.
} else if let vidData = media.videoURL {
print("VIDEO")
let autoIDSto = "media\(media.numMedia).mov"
print(autoIDSto)
let autoID = "media\(media.numMedia)"
let storageRef = Storage.storage().reference().child((Auth.auth().currentUser?.uid)!).child("post:\(postID)").child(autoIDSto)
let postRef = childRef.child("Media")
let uploadData = media.videoURL
let uploadTask = storageRef.putFile(from: vidData, metadata: nil) { (metadata, error) in
print("\(vidData) : Video data")
guard let metadata = metadata else { return }
if let error = error {
print(error)
}
storageRef.downloadURL(completion: { (url, error) in
//a bunch of code to add to DB
if let thumbnailImageData = media.thumbnailImage!.jpegData(compressionQuality: 1.0) {
storageRef.putData(thumbnailImageData, metadata: nil) { (metadata, error) in
storageRef.downloadURL(completion: { (url, error) in
if let thumbnail = url {
mediaRef.updateChildValues(["thumbnail" : "\(thumbnail)"])
Whats wrong and how can I fix it?
You use the same path ( 1 with mov extension )
let storageRef = Storage.storage().reference().child((Auth.auth().currentUser?.uid)!).child("post:\(postID)").child(autoIDSto)
to store the video/image thumbnail here
let uploadTask = storageRef.putFile(from: vidData, metadata: nil) { (metadata, error) in
and
storageRef.putData(thumbnailImageData, metadata: nil) { (metadata, error) in
There should be 2 different paths 1 with mov extension and another with jpg extension that you finally store the reference of both in 1 record in your database table

ImageView to Firebase Storage in Swift

I am trying to upload an image from ImageView to Firebase storage but it won't work.
I have listed my code below: My image view is called ProfileImage
let storageRef = Storage.storage().reference().child("myImage.png")
if let uploadData = self.ProfileImage.image!.pngData() {
storageRef.putFile(from: uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print("error")
completion(nil)
} else {
// your uploaded photo url.
}
}
}
It comes up with the error "Cannot convert value of type 'Data' to expected argument type 'URL'
You are trying to upload Data, not a file. Replace
putFile
With
putData
And it should work fine
Try this code :-
let storageRef = Storage.storage().reference().child("myImage.png")
if let uploadData = UIImagePNGRepresentation(self.profileImage.image!) {
storageRef.put(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print("\(error.localizeDescription)")
} else {
// your uploaded photo url.
}
}
let refDatabase = Database.database().reference()
var refstorage = Storage.storage().reference()
let data = image.jpegData(compressionQuality: 1.0) //
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
let postkey = refDatabase.child("Post").childByAutoId().key
print(postkey)
let imagename = "PostImage/\(postkey).png"
refstorage = refstorage.child(imagename)
let timestamp = Date().timeIntervalSince1970 // you can use this to track time when it was uploaded.
self.refstorage?.putData(data!, metadata: metadata, completion: { (meta, error) in
if error == nil{
if let imageData = meta?.downloadURL()?.absoluteString{
// DO SOMETHING
} else {
print("Couldn't get the URL for image")
}
}
})
I know that this question has been answered, but there is an easier way to do this. Along with import Firebase and Firebase Storage, you will also have to add FirebaseUI to your podfile and import it.
After you have done that, you could get your image to your app much simpler.
let storage = Storage.storage()
let storageRef = storage.reference()
let placeholderImage = UIImage(named: "placeholder.jpeg")
let reference = storageRef.child("myImage.png")
ProfileImage.sd_setImage(with: reference, placeholderImage: placholderImage)
(The placeholder Image would just be a transparent image that you put in your assets folder in XCode that you could reuse multiple times in your application, for whenever you needed to get a Firebase Image on your app.)

Getting empty result when trying to fetch the output of AVAssetExportSession from PHAsset

I'm attempting to build a small toy iOS app that merges two video assets from the user's photos library. I've gotten to the point where I have merged the videos using an AVMutableComposition instance and now I need to export the composition. I am doing so with the following code:
func saveEditedComposition(_ composition: AVMutableComposition) {
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let savePath = (documentDirectory as NSString).strings(byAppendingPaths: ["mergeVideo.mov"])[0]
let url = NSURL(fileURLWithPath: savePath)
// Set up exporter
guard let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) else { return }
exporter.outputURL = url as URL
exporter.outputFileType = AVFileTypeQuickTimeMovie
exporter.shouldOptimizeForNetworkUse = true
// Perform the export
exporter.exportAsynchronously(completionHandler: { () -> Void in
// Upon completion of the export,
DispatchQueue.global().async {
if exporter.status == .completed {
let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [exporter.outputURL!], options: nil))
let phAsset = fetchResult.firstObject! // Crashes here, returning nil.
}
}
})
}
The problem is, when the completionHandler is run, I am able to see that my exporter has completed without error (if exporter.status == .completed {), but when I try to access the asset at exporter.outputURL, it returns an empty PHFetchResult<PHAsset>. Can anyone see what I'm doing wrong here?
You're exporting the video to the file system (the Documents directory), yet fetching from the camera roll/ALAssetsLibrary.
It depends on what you need to do with the exported file. If it needs to be in the ALAssetsLibrary, you can use
ALAssetsLibrary().writeVideoAtPath(toSavedPhotosAlbum: exporter.outputURL!) { alAssetURL, error in
}
However ALAssetsLibrary is now deprecated and you are supposed to use PHPhotoLibrary. I don't know the Photos framework, but I think it works like this:
var placeHolder: PHObjectPlaceholder?
PHPhotoLibrary.shared().performChanges({
let changeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: exporter.outputURL!)
if let changeRequest = changeRequest {
// maybe set date, location & favouriteness here?
placeHolder = changeRequest.placeholderForCreatedAsset
}
}) { success, error in
placeHolder?.localIdentifier // should identify asset from now on?
}
If you don't need the video to live in the camera roll, then you can work directly with the file in exporter.outputURL!.

Resources