Swift 3 Google Firebase Multi Upload - swift

i am currently trying to upload multiple file from my device to google firebase, here is my setup:
func upload(completionHandler: #escaping (Bool) -> ()) {
let fileNames = try fileManager.contentsOfDirectory(atPath: documentsUrl.path)
for fileName in fileNames {
let url = URL(fileURLWithPath: "\(FileService.shared.documentsUrl.path)/\(fileName)")
let ref = storage.reference().child("path/\(fileName)")
let upload = ref.putFile(from: url, metadata: nil, completion: { (metadata, error) in
if error != nil {
completionHandler(true)
}
completionHandler(true)
})
}
}
How i call it:
self.upload { (success) in
print("operation \(success)")
}
What i see:
When i just have one file in the document directory, the loop runs just once and the file is being uploaded!
BUT: When there are multiple files, the loops also runs multiple times and the files are NOT being uploaded!
FIRStorageErrorDomain Code=-13000
Anybody could help me with this?
Thanks and Greetings!

Related

Upload Images from Array to Firebase & Get Download URL

I am trying to upload an array of images to firebase storage and return an array of images so i can add it to the database as a post.
i have tried to upload the image then the post but the code is executing twice if i have two images and inserting two entries into the database. I am using the function below to upload an image.
var data = [[String:String]]
for image in data {
uploadMedia {
// returns single urls
}
}
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.
}
}
}
I want the function to return an array of images such as [imageURL:"XYZ" , size : "Size"]

Create Zip file from an array of data (coreData Images) and share via the .zip file via Email

I have an (n) of data (UIImage JPEG) inside my CoreData.
let imageData: [Data]...
I have already this two frameworks/ Pods: Zip and ZIPFoundation
I have a few Question about that:
I need to create a temp URL for each of my imageData?
If yes, I have to add tempURL.appendingPathExtension("jpg") to each temp URLs before or after call data.write(to: tempURL) ?
After that, I have an Array of URLs, so I just need to create a Zip File and share it. But it doesn't work, I get a .zip - .cpgz Loop on my Mac.
private func createURLsFrom(imageData: [ImageData]?) {
var urlArray = [URL]()
imageData?.forEach { imData in
if let data = imData.imageData,
let tempURL = NSURL.fileURL(withPathComponents: [NSTemporaryDirectory(), NSUUID().uuidString])?.appendingPathExtension("jpg") {
do {
try data.write(to: tempURL)
urlArray.append(tempURL)
} catch {...}
}
}
self.createZipFile(urlArray: urlArray)
}
private func createZipFile(urlArray: [URL]) {
if let zipURL = try? Zip.quickZipFiles(urlArray, fileName: "ZIP_Test1") {
self.sendEmailWith(dataURL: zipURL)
} else {...}
}
private func sendEmailWith(dataURL: URL) {
if MFMailComposeViewController.canSendMail() {
let mailComposer = MFMailComposeViewController()
mailComposer.mailComposeDelegate = self
mailComposer.setSubject("setSubject")
mailComposer.setMessageBody("setMessageBody", isHTML: false)
mailComposer.addAttachmentData(dataURL.dataRepresentation, mimeType: "application/zip", fileName: ("ZIP_Test1.zip"))
self.present(mailComposer, animated: true, completion: nil)
}
}
What am I doing wrong :(
It's a bit lengthy, and––disclaimer––untested. Let me know if it works or if you have any questions.
Create a temp directory for all the files:
func createTempDirectory() -> URL? {
if let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let dir = documentDirectory.appendingPathComponent("temp-dir-\(UUID().uuidString)")
do {
try FileManager.default.createDirectory(atPath: dir.path, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error.localizedDescription)
}
return dir
} else {
return nil
}
}
Save all the images to the temp directory:
func saveImages(data: [Data]) -> URL? {
guard let directory = createTempDirectory() else { return nil }
do {
for (i, imageData) in data.enumerated() {
try imageData.write(to: directory.appendingPathComponent("image\(i).jpg"))
}
return directory
} catch {
return nil
}
}
Get the URL for the zipped file. This is an optional in case an error occurred along the way. Also, done on a background thread because it could take a bit of time, and you don't want to block the main thread.
func zipImages(data: [Data], completion: #escaping ((URL?) -> ())) {
DispatchQueue.main.async {
guard let directory = saveImages(data: data) else {
completion(nil)
return
}
do {
let zipFilePath = try Zip.quickZipFiles([directory], fileName: "archive-\(UUID().uuidString)")
completion(zipFilePath)
} catch {
completion(nil)
}
}
}
After you send the file, you'll probably want to delete the temp directory so your app size doesn't start growing.
After a lot of debugging finally, I found the problem.
First of all Daniels answer was correct too.
But the main issue was my sendEmailWith function, "application/zip" is not working!
So I added UIActivityViewController with the directory URL and it's work.
Now I can share my Zip file via Mail and Airdrop!
Move the
self.createZipFile(urlArray: urlArray)
to after the forEach loop.

What is the downloadURL equivalent in FirebaseStorage 3? [duplicate]

I just updated Firebase Storage to 5.0.0 and it looks like metadata.downloadURL() is not recognized anymore. (Value of type 'StorageMetadata' has no member 'downloadURL')
Though after looking in the documentation it should still be available :
https://firebase.google.com/docs/reference/swift/firebasestorage/api/reference/Classes/StorageMetadata#/c:objc(cs)FIRStorageMetadata(im)downloadURL
The project was cleaned & rebuilt already.
Am I missing something ?
Can you try Google Firebase docs
// Create a reference to the file you want to download
let starsRef = storageRef.child("images/stars.jpg")
// Fetch the download URL
starsRef.downloadURL { url, error in
if let error = error {
// Handle any errors
} else {
// Get the download URL for 'images/stars.jpg'
}
}
This is my version for Swift 3 / Swift 4.
Explanation of what happens in the code.
This is essentially the same answer as Sh_Khan's. But in his example the User already knows the bucket path. In my example, we get the path from an upload task. This was what has lead me to this question as well as what I think op was looking for as he was looking for metadata.downloadURL() replacement.
class StorageManagager {
private let storageReference: StorageReference
init() {
// first we create a reference to our storage
// replace the URL with your firebase URL
self.storageReference = Storage.storage().reference(forURL: "gs://MYAPP.appspot.com")
}
// MARK: - UPLOAD DATA
open func uploadData(_ data: Data, named filename: String, completion: #escaping (URL? , Error?) -> Void) {
let reference = self.storageReference.child(filename)
let metadata = StorageMetadata()
metadata.contentType = "ourType" // in my example this was "PDF"
// we create an upload task using our reference and upload the
// data using the metadata object
let uploadTask = reference.putData(data, metadata: metadata) { metadata, error in
// first we check if the error is nil
if let error = error {
completion(nil, error)
return
}
// then we check if the metadata and path exists
// if the error was nil, we expect the metadata and path to exist
// therefore if not, we return an error
guard let metadata = metadata, let path = metadata.path else {
completion(nil, NSError(domain: "core", code: 0, userInfo: [NSLocalizedDescriptionKey: "Unexpected error. Path is nil."]))
return
}
// now we get the download url using the path
// and the basic reference object (without child paths)
self.getDownloadURL(from: path, completion: completion)
}
// further we are able to use the uploadTask for example to
// to get the progress
}
// MARK: - GET DOWNLOAD URL
private func getDownloadURL(from path: String, completion: #escaping (URL?, Error?) -> Void) {
self.storageReference.child(path).downloadURL(completion: completion)
}
}
Let's try this code in Swift 4.2:
let imgData = UIImage.jpegData(self.imageView.image!)
let imageName = UUID().uuidString
let ref = Storage.storage().reference().child("pictures/\(imageName).jpg")
let meta = StorageMetadata()
meta.contentType = "image/jpeg"
self.uploadToCloud(data: imgData(0.5)!, ref: ref, meta: meta)
UploadToCloud Method:
` Method UploadToCloud
func uploadToCloud(data:Data, ref:StorageReference, meta:StorageMetadata) {
ref.putData(data, metadata: meta) { (metaData, error) in
if let e = error {
print("==> error: \(e.localizedDescription)")
}
else
{
ref.downloadURL(completion: { (url, error) in
print("Image URL: \((url?.absoluteString)!)")
})
}
}
}
This question pops up for all language searches. Hence for Kotlin, the solution is something of the kind below:
val photoRef = FirebaseStorage.getInstance()
.reference.child("images/stars.jpg")
// Code ommited - Do some saving - putFile
photoRef.downloadUrl.addOnSuccessListener({ uri ->
product.imageUrl = uri.toString()
})
However, this is not a good solution. You are better off saving the path and then re-constructing the full Url on demand. For example:
photoRef.downloadUrl.addOnSuccessListener({ uri ->
val imagePath = uri.toString()
// Save to database
})
Now, you can use it later, and only on demand:
FirebaseStorage.getInstance().reference.child(product.imageUrl).downloadUrl
.addOnSuccessListener { uri ->
String imageUrl = uri.toString()
// Load in images
}
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
if error != nil {
print(error as Any)
return
}
guard let uid = user?.user.uid else {
return
}
self.dismiss(animated: true, completion: nil)
//Добавляем картинку в firebase. Надо добавить в Pods file pod 'Firebase/Storage' и запустить терминал
let imageName = NSUUID().uuidString
let storageRef = Storage.storage().reference()
// Create a reference to the file you want to download
let starsRef = storageRef.child("profile_images").child("\(imageName).png")
let uploadData = self.profileImageView.image?.pngData()
starsRef.putData(uploadData!, metadata: nil, completion: { (metadata, error) in
if error != nil {
print(error as Any)
}
if let profileImageUrl = metadata?.path {
let values = ["name": name, "email": email, "profileImage": profileImageUrl]
self.registerUserIntoDatabaseWithUID(uid: uid, values: values)
}
})
}
If you are stuck in converting URL to string... you can try this
url.absoluteString

App freezes / locks up when writing very large files

I'm downloading and writing ~200mb podcasts into the Documents directory with the following code:
var podcastRequest = NSURLRequest(URL: audioUrl)
NSURLConnection.sendAsynchronousRequest(podcastRequest, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
if let myPodcastDataFromUrl = NSData(contentsOfURL: audioUrl) {
if myPodcastDataFromUrl.writeToURL(destinationUrl, atomically: true) {
// add to the array to track the download
var tempDic = self.posts[theRow] as! NSMutableDictionary as NSMutableDictionary
tempDic["downloaded"] = "true"
self.posts[theRow] = tempDic
} else {
println("Error saving file")
}
}
})
The sendAsynchronousRequest call prevents the lockup from happening during the download, but the app still freezes when it starts actually writing it to the directory.
Is there a way to prevent the lockup from happening at all, or am I going to have to write smaller chunks at a time?
You won't be able to store 200MB in memory before trying to write it to disk, but you can use downloadTaskWithURL method, it writes the file to a temporary folder and you can move it when it finishes to the documents folder as follow.
let documentsDirectoryURL = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first as! NSURL
NSURLSession.sharedSession().downloadTaskWithURL(audioUrl, completionHandler: {
(location, response, error) -> Void in
if let error = error {
println(error.description)
}
else {
if NSFileManager().moveItemAtURL(location, toURL: documentsDirectoryURL.URLByAppendingPathComponent(response.suggestedFilename!), error: nil) {
println("saved")
} else {
println("not saved.")
}
}
}).resume()

Swift Google Drive downloading file

I can't figure out how to download a Google Drive file in Swift. I followed the modified quickstart from Google Objective-C API 'GTL' with Swift and that worked. I can't translate the objective C code from the google drive API on downloading files. I've searched around and can't find anything. How can I get this to work?
You can use this function for downloading files with the Google Drive API in Swift:
func downloadFile(file: GTLDriveFile){
let url = "https://www.googleapis.com/drive/v3/files/\(file.identifier!)?alt=media"
let fetcher = drive.fetcherService.fetcherWithURLString(url)
fetcher.beginFetchWithDelegate(
self,
didFinishSelector: #selector(ViewController.finishedFileDownload(_:finishedWithData:error:)))
}
(In this case drive is the GTLServiceDrive - the same as in the Documentation)
Then you need to implement the function finishedFileDownload that will be called once the download is completed:
func finishedFileDownload(fetcher: GTMSessionFetcher, finishedWithData data: NSData, error: NSError?){
if let error = error {
//show an alert with the error message or something similar
return
}
//do something with data (save it...)
}
Actual for Swift 5.
func download(file: GTLRDrive_File) {
let url = "https://www.googleapis.com/drive/v3/files/\(file.identifier!)?alt=media"
let fetcher = drive.fetcherService.fetcher(withURLString: url)
fetcher.beginFetch(completionHandler: { data, error in
if let error = error {
print(error.localizedDescription)
}
//Do something with data
})
}
Swift 5 with progress block. file.size returns nil for some reason so I used fetcher.response?.expectedContentLength instead.
func download(file: GTLRDrive_File, service: GTLRDriveService) {
let url = "https://www.googleapis.com/drive/v3/files/\(file.identifier)?alt=media"
let fetcher = service.fetcherService.fetcher(withURLString: url)
fetcher.beginFetch(completionHandler: { fileData, error in
if error == nil {
print("finished downloading Data...")
print(fileData as Any)
} else {
print("Error: \(String(describing: error?.localizedDescription))")
}
})
fetcher.receivedProgressBlock = { _, totalBytesReceived in
if let fileSize = fetcher.response?.expectedContentLength {
let progress: Double = Double(totalBytesReceived) / Double(fileSize)
// update progress bar here
print(progress)
}
}
}