Firebase Storage and Swift - download task handling - swift

I have a download task to get the video file. I checked the documentation about the download task but I don't see anything about handling the existing files.
Does the download task checks the existing file, then checks if it need to be resumed or leaves it alone?
If the download task fails in the middle of downloading, will the incomplete file still there?
If it don't overwrite the file, how do I compare the files in local and remote (file size? or MD5) to verify that I don't need to download the file?
Scenario - I have AppDelegateManager to download all of the videos beforehand. I don't want to download the videos each time the app is opened and wasn't sure if the videos were overwritten or not.
func getVideo(fileName : String, localUrl : URL, completion : #escaping (_ success : Bool) -> Void) {
let storage = Storage.storage()
let pathReference = storage.reference(withPath: FirebaseDirectoryNames.videos.rawValue + "/" + fileName)
let downloadTask = pathReference.write(toFile: localUrl) { url, error in
if let error = error {
os_log("error : ", log: self.tag, type: .debug, error.localizedDescription)
completion(false)
} else {
completion(true)
}
}
}

Related

Auto create directory when writing a file to a directory that does not exist

I am using File Manager to write files to the users documents directory. Every file is downloaded to a users device via a URL and then I create a local URL by doing the following:
extension URL {
func getLocalUrl() -> URL {
let directoryURL = FileManager.default.getDocumentsDirectory()
let filePath = pathComponents.dropFirst().joined(separator: "-")
return directoryURL.appendingPathComponent(filePath)
}
}
This works perfectly fine. However, when I try to create a local URL by using slashes instead of dashes via the following:
extension URL {
func getLocalUrl() -> URL {
let directoryURL = FileManager.default.getDocumentsDirectory()
let filePath = pathComponents.dropFirst().joined(separator: "/")
return directoryURL.appendingPathComponent(filePath)
}
}
I get the following error when this code runs:
func save(url: URL, fileUrl: URL) {
do {
// fileUrl is a url in the temporary directory from a URLSession.downloadTask
try FileManager.default.moveItem(at: fileUrl, to: url.getLocalUrl())
} catch {
print("download.service.write.error: \(error)")
}
}
CFNetworkDownload_sKaBto.tmp” couldn’t be moved to “user-data” because either the former doesn’t exist, or the folder containing the latter doesn’t exist."
As you can see the error is because I am trying to write to a directory that does not exist. Is there a way to auto create the directory if it does not exist?

How to download all files from dropbox synchronously in swift?

Using SwiftyDropbox download method to download all files. But, i found progress block showing data all together of different files, not downloading files in a way that downloading of one file start after completing the previous one.
Here, is the code used :
DropboxClientsManager.authorizedClient?.files
.download(path: entry.pathLower!, overwrite: true, destination: destination)
.response { response, error in
if let response = response {
print(response)
}
else if let error = error {
print(error)
}
}
.progress { progressData in
print(progressData)
}

FileManager and accessing app group error

I've been having problem with using share extension to get a file and unzip it into my app group folder.
Here is the code:
for attachment in content.attachments as! [NSItemProvider] {
if attachment.hasItemConformingToTypeIdentifier(contentType){
attachment.loadItem(forTypeIdentifier: contentType, options: nil) { (data, error) in
let fileManager = FileManager()
let url = data as! URL
let destinationURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.KakaoAnalyzer")?.appendingPathComponent("KakaoFilesTemp")
do {
try fileManager.createDirectory(at: destinationURL!, withIntermediateDirectories: true, attributes: nil)
try fileManager.unzipItem(at: url, to: destinationURL!)
self.mergeFiles()
print("SUCCESSFULLY UNZIPPED")
} catch {
print(error.localizedDescription)
print("UNZIP FAILED")
}
}
}
}
This is part of my didSelectPost()
I'm using share extension to get a ZIP file via share, and ZIPFoundation to unzip it into my destination folder which is my app group with this extension and my main project.
This code runs fine and unzips successfully in a simulator, but when I run it on my phone, I get an error:
The file "FILENAME" couldn't be opened because there is no such file
when there clearly should be one, and
Failed to determine whether URL "FILEURL" is managed by a file provider.
I've tried moving the file at the url to my documentDirectory, but it produces error:
"FILE" couldn't be moved because you don't have permission to access "appGroupFolder"
Does anyone how to solve this? Any help would be greatly appreciated!

How to Use SwiftyDropbox's "destination" with a Download

In reviewing the SwiftyDropbox tutorial in the v2 Dropbox API, it shows how to perform a download:
// Download a file
let destination : (NSURL, NSHTTPURLResponse) -> NSURL = { temporaryURL, response in
let fileManager = NSFileManager.defaultManager()
let directoryURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
// generate a unique name for this file in case we've seen it before
let UUID = NSUUID().UUIDString
let pathComponent = "\(UUID)-\(response.suggestedFilename!)"
return directoryURL.URLByAppendingPathComponent(pathComponent)
}
client.files.download(path: "/MyFile.db", destination: destination).response { response, error in
if let (metadata, url) = response {
print("*** Download file ***")
let data = NSData(contentsOfURL: url)
print("Downloaded file name: \(metadata.name)")
print("Downloaded file url: \(url)")
print("Downloaded file data: \(data)")
} else {
print(error!)
}
}
I'm unclear what's going on with the destination part. Why do I need to generate a random string for the filename?
When I try to specify my own filename, the download doesn't seem to work:
let destination : (NSURL, NSHTTPURLResponse) -> NSURL = { temporaryURL, response in
let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
return directoryURL.URLByAppendingPathComponent("MyFile.db")
}
I want to download a file from Dropbox named MyFile.db and I want to put it in my device's documents directory with the name MyFile.db and overwrite it if it's already there.
How can I do that?
When you say it doesn't seem to work, I expect you mean you get an error like this:
Error Domain=NSCocoaErrorDomain Code=516 "“CFNetworkDownload_bPYhu1.tmp” couldn’t be moved to “Documents” because an item with the same name already exists." UserInfo={NSSourceFilePathErrorKey=..., NSUserStringVariant=(
Move
), NSDestinationFilePath=..., NSUnderlyingError=0x7fda0a67cea0 {Error Domain=NSPOSIXErrorDomain Code=17 "File exists"}}
SwiftyDropbox, by virtue of using AlamoFire, doesn't currently let you overwrite files using the download function.
Specifically, SwiftyDropbox calls download in AlamoFire, and AlamoFire calls NSFileManager.moveItemAtURL. The documentation for NSFileManager.moveItemAtURL says:
If an item with the same name already exists at dstURL, this method aborts the move attempt and returns an appropriate error.
So, it seems like it's just being cautious, and making it hard for your app to accidentally overwrite (ad potentially lose) data. If you definitely know you want to overwrite a particular file, you'll need to do so explicitly, after the Dropbox API call. We'll consider this a feature request though.
Update: SwiftyDropbox now offers the ability to overwrite the files directly as of version 3.1.0, e.g., using download(path:rev:overwrite:destination:).

Uploading a video to S3 with swift The operation couldn’t be completed. (Cocoa error 260.)"

I am trying to upload a video file to Amazon S3
I constantly get an error 260:
Error in uploading the video: Error
Domain=NSCocoaErrorDomain Code=260 "The operation couldn’t be
completed. (Cocoa error 260.)
I read somewhere that amazon does not support asset library - is it true? and if so what do you suggest
Thanks Eran
func saveVideoToS3 () {
var uploadRequest: AWSS3TransferManagerUploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest.bucket = "BucketName"
uploadRequest.key = "KeyText"
//Move video file to the application folder so it can be read
var savedVideoURLToBeUsed = NSUserDefaults.standardUserDefaults().objectForKey("ThisIsTheVideoIWantToUse") as! String
println("Video saved in Store: \(savedVideoURLToBeUsed)")
var url: NSURL = NSURL(fileURLWithPath: savedVideoURLToBeUsed)!
uploadRequest.body = url
//uploadRequest.body = NSURL(fileURLWithPath: "file:///\(url)")
println("URL: \(url)")
let transferManager: AWSS3TransferManager = AWSS3TransferManager.defaultS3TransferManager()
transferManager.upload(uploadRequest).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: { (AWSTask) -> AnyObject! in
//Handle errors
if AWSTask.error != nil {
println("Error in uploading the video: \(AWSTask.error)")
if AWSTask.error.code == 1001 {
self.saveVideoToS3()
}
// Retrive information important for later downloading
} else {
println("Video upload successful..")
var uploadResult: AnyObject! = AWSTask.result
println("Upload result: \(uploadResult)")
//Delete file from the application folder
}
return nil
})
}
Cocoa error 260 is a NSFileReadNoSuchFileError, meaning the path you specified is not valid (file is just not where you say it is), so it probably has nothing with S3 itself. There are three things why this is happening that come to my mind:
you did not use .synchronize() when saving the key to user settings
your file URL contains invalid characters
you did not write the file into filesystem properly
iOS8 Breaking change
Also please note that as of iOS8, due to changes how application work with their assigned sandboxes, you can't save absolute URL to file because next time you open application, it will be different.
Beginning in iOS 8, the Documents and Library directories are no
longer siblings of your application's bundle.
I am using two quick convenience functions that I wrote to get file from cache directory:
func cachePathWithFileName(fileName : String) -> String {
let cacheDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String
return cacheDirectoryPath.stringByAppendingPathComponent(fileName)
}
and documents directory:
func documentsPathWithFileName(fileName : String) -> String {
let documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] as! String
return documentsDirectoryPath.stringByAppendingPathComponent(fileName)
}
Hopefully some of this tips help!