How to edit the file in swiftyDropbox? - ios

In my project, I uploaded all my text files to the Dropbox. Here, I need to directly edit the uploaded file in Dropbox using swift. Is it possible to edit files in Dropbox?
(OR) Is it possible to overwrite the same fileName in Dropbox?

Solution moved from #SathishKumarGurunathan's question post.
I found the answer and I will share here.
_ = client?.files.upload(path: path, mode: .overwrite, autorename: false, clientModified: nil, mute: false, input: Description).response { response, error in
if let response = response {
print("The upload response is \(response)")
} else if let error = error {
print("The upload error is \(error)")
}
}
.progress { progressData in
print("The progress Data is \(progressData)")
}

Related

How do you allow very large files to have time to upload to firebase before iOS terminates the task?

I have a video sharing app, and when you save a video to firebase storage it works perfectly for videos that are roughly 1 minute or shorter.
The problem that I am having, is when I try to post a longer video (1 min or greater) it never saves to firebase.
The only thing that I can think of is this error that I am getting, and this error only shows up about 30 seconds after I click the save button:
[BackgroundTask] Background Task 101 ("GTMSessionFetcher-firebasestorage.googleapis.com"), was created over 30 seconds ago. In applications running in the background, this creates a risk of termination. Remember to call UIApplication.endBackgroundTask(_:) for your task in a timely manner to avoid this.
Here is my code to save the video to firebase.
func saveMovie(path: String, file: String, url: URL) {
var backgroundTaskID: UIBackgroundTaskIdentifier?
// Perform the task on a background queue.
DispatchQueue.global().async {
// Request the task asseration and save the ID
backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "Finish doing this task", expirationHandler: {
// End the task if time expires
UIApplication.shared.endBackgroundTask(backgroundTaskID!)
backgroundTaskID = UIBackgroundTaskIdentifier.invalid
})
// Send the data synchronously
do {
let movieData = try Data(contentsOf: url)
self.storage.child(path).child("\(file).m4v").putData(movieData)
} catch let error {
fatalError("Error saving movie in saveMovie func. \(error.localizedDescription)")
}
//End the task assertion
UIApplication.shared.endBackgroundTask(backgroundTaskID!)
backgroundTaskID = UIBackgroundTaskIdentifier.invalid
}
}
Any suggestions on how I can allow my video time to upload?
Finally figured this out after a long time...
All you have to do is use .putFile("FileURL") instead of .putdata("Data"). Firebase documentation says you should use putFile() instead of putData() when uploading large files.
But the hard part is for some reason you can't directly upload the movie URL that you get from the didFinishPickingMediaWithInfo function and firebase will just give you an error. So what I did instead was get the data of the movie, save the movie data to a path in the file manager, and use the file manager path URL to upload directly to firebase which worked for me.
//Save movie to Firestore
do {
// Convert movie to Data.
let movieData = try Data(contentsOf: movie)
// Get path so we can save movieData into fileManager and upload to firebase because movie URL does not work, but fileManager url does work.
guard let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent(postId!) else { print("Error saving to file manager in addPost func"); return }
do {
try movieData.write(to: path)
// Save the file manager url file to firebase storage
Storage.storage().reference().child("Videos").child("\(postId!).m4v").putFile(from: path, metadata: nil) { metadata, error in
if let error = error {
print("There was an error \(error.localizedDescription)")
} else {
print("Video successfully uploaded.")
}
// Delete video from filemanager because it would take up too much space to save all videos to file manager.
do {
try FileManager.default.removeItem(atPath: path.path)
} catch let error {
print("Error deleting from file manager in addPost func \(error.localizedDescription)")
}
}
} catch let error {
print("Error writing movieData to firebase \(error.localizedDescription)")
}
} catch let error {
print("There was an error adding video in addPost func \(error.localizedDescription)")
}

Failed to download url

Okay, so basically I'm creating a messenger app which I want to be able to upload and load a profile picture. Currently I can successfully upload a picture to firebase, but as stated in the title, it fails to download the picture URL.
(Tutorial that I am watching: https://www.youtube.com/watch?v=Hmr8PsG9E2w&list=PL5PR3UyfTWvdlk-Qi-dPtJmjTj-2YIMMf&index=18&ab_channel=iOSAcademy) 15-20 min into the video.
Where the error occurs:
strongSelf.storage.child("images/"+fileName).downloadURL(completion: {url, error in
guard let url = url else {
print("Failed to get download url")
completion(.failure(StorageErrors.failedToGetDownloadUrl))
return
}
let safeEmail = DatabaseManager.safeEmail(emailAddress: email)
let filename = safeEmail + "_profile_picture.png"
let path = "images/"+filename
Error message in console
What I've tried:
Changing and double checking that the path leads to the picture.
Trying different tactics of calling path like ("(path)") instead of (path)
Other random stuff that I found looking for this problem on the web
What my conclusion so far is that the program does not seem to indentify the correct path to the picture, but I've made sure that it looks identical to the video.
Would be awesome if anyone knows or has any idea how to deal with this issue? This is my first post so please tell me if more information is needed regarding the code is needed.
Cheers // Jakob
Adding extra to comments:
So the (error) is : failedToGetDownloadUrl
So this is where I suspect there is something going wrong:
strongSelf.storage.child("images/\(fileName)").downloadURL(completion: { url, error in
guard let url = url else {
print("Failed to get download url")
completion(.failure(StorageErrors.failedToGetDownloadUrl))
return
}
let urlString = url.absoluteString
print("download url returned: \(urlString)")
completion(.success(urlString))
})
})
}
And here are the Path to the images:
let safeEmail = DatabaseManager.safeEmail(emailAddress: email)
let filename = safeEmail + "_profile_picture.png"
let path = "images"+filename
And this is my firebase storage:

SWIFTUI - File Not Found error when trying to import a file from a cloud file provider like OneDrive and GoogleDrive

I have the following SwiftUI code where a simple button brings up the iOS file manager and allows the user to select a CSV file to be imported. I've found that it works well for files that are stored locally on my device but if I try to select a file from Google Drive or OneDrive it gets a URL but when I then try to retrieve the data from it, it returns an error saying that the file was not found.
After a lot of head scratching, I've found that when using the file browser if I long press to bring up the context menu and then view the info for the file (which I'm guessing may be pulling it down to the phones local cache), it will then work as expected. This is shown in the following animated gif:
I've found that once I've done that caching trick, I can access the file without issue in other apps using the same code and I've also found that I can uninstall my app and reinstall it and it continues to work.
Can anyone advise on an approach using SwiftUI where I can avoid this File Not Found error when trying to import the file from Google Drive or OneDrive?
The entire code that I've been using for testing is as follows:
import SwiftUI
struct ContentView: View {
#State private var isImporting: Bool = false
#State private var fileContentString = ""
#State var alertMsg = ""
#State var showAlert = false
func reportError(error: String) {
alertMsg = error
showAlert.toggle()
}
var body: some View {
VStack {
Button(action: { isImporting = true}, label: {
Text("Select CSV File")
})
.padding()
Text(fileContentString) //This will display the imported CSV as text in the view.
}
.padding()
.fileImporter(
isPresented: $isImporting,
allowedContentTypes: [.commaSeparatedText],
allowsMultipleSelection: false
) { result in
do {
guard let selectedFileURL: URL = try result.get().first else {
alertMsg = "ERROR: Result.get() failed"
self.reportError(error: alertMsg)
return
}
print("selectedFileURL is \(selectedFileURL)")
if selectedFileURL.startAccessingSecurityScopedResource() {
//print("startAccessingSecurityScopedResource passed")
do {
print("Getting Data from URL...")
let inputData = try Data(contentsOf: selectedFileURL)
print("Converting data to string...")
let inputString = String(decoding: inputData, as: UTF8.self)
print(inputString)
fileContentString = inputString
}
catch {
alertMsg = "ERROR: \(error.localizedDescription)"
self.reportError(error: alertMsg)
print(alertMsg)
}
//defer { selectedFileURL.stopAccessingSecurityScopedResource() }
} else {
// Handle denied access
alertMsg = "ERROR: Unable to read file contents - Access Denied"
self.reportError(error: alertMsg)
print(alertMsg)
}
} catch {
// Handle failure.
alertMsg = "ERROR: Unable to read file contents - \(error.localizedDescription)"
self.reportError(error: alertMsg)
print(alertMsg)
}
}
.alert(isPresented: $showAlert, content: {
Alert(title: Text("Message"), message: Text(alertMsg), dismissButton: .destructive(Text("OK"), action: {
}))
})
}
}
The console log output is as follows:
selectedFileURL is file:///private/var/mobile/Containers/Shared/AppGroup/8F147702-8630-423B-9DA0-AE49667748EB/File%20Provider%20Storage/84645546/1aTSCPGxY3HzILlCIFlMRtx4eEWDZ2JAq/example4.csv
Getting Data from URL...
ERROR: The file “example4.csv” couldn’t be opened because there is no such file.
selectedFileURL is file:///private/var/mobile/Containers/Shared/AppGroup/8F147702-8630-423B-9DA0-AE49667748EB/File%20Provider%20Storage/84645546/1aTSCPGxY3HzILlCIFlMRtx4eEWDZ2JAq/example4.csv
Getting Data from URL...
Converting data to string...
First Name,Last Name
Luke,Skywalker
Darth,Vader
My testing has been done on a physical iPhone 12 Pro Max running iOS 14.2 and a physical iPad Air 2 running iPadOS 14.4.
I found an answer to my issue. The solution was to use a NSFileCoordinator() to force the file to be downloaded.
With the code below, if I access a file in cloud storage that hasn't been previously downloaded to the local device it will print "FILE NOT AVAILABLE" but it will now just download the file rather than throwing a file not found error.
Ideally I would like to be able to download just the file property metadata first to check how big the file is and then decide if I want to download the full file. The NSFileCoordinator has a metadata only option but I haven't worked out how to retrieve and interpret the results from that. This will do for now...
if selectedFileURL.startAccessingSecurityScopedResource() {
let fileManager = FileManager.default
if fileManager.fileExists(atPath: selectedFileURL.path) {
print("FILE AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
var error: NSError?
NSFileCoordinator().coordinate(
readingItemAt: selectedFileURL, options: .forUploading, error: &error) { url in
print("coordinated URL", url)
let coordinatedURL = url
isShowingFileDetails = false
importedFileURL = selectedFileURL
do {
let resources = try selectedFileURL.resourceValues(forKeys:[.fileSizeKey])
let fileSize = resources.fileSize!
print ("File Size is \(fileSize)")
} catch {
print("Error: \(error)")
}
}
do {
print("Getting Data from URL...")
let inputData = try Data(contentsOf: selectedFileURL)
print("Do stuff with file....")
}
}

Swift File Download Issue

I am trying to download a plist file from a remote location and use it in the iOS app I am creating. The file is going to be used for calendar details within the app's calendar. The goal is obviously that I can update the remote file instead of having to push updates to the app itself every time we need to make changes to calendar details.
I started with the code used in this example: Download File From A Remote URL
Here is my modified version:
// Create destination URL
let documentsUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("2017.plist")
//let destinationFileUrl = URL(string: Bundle.main.path(forResource: String(currentYear), ofType: "plist")!)
//Create URL to the source file you want to download
let fileURL = URL(string: "https://drive.google.com/open?id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.removeItem(at: destinationFileUrl)
try FileManager.default.moveItem(at: tempLocalUrl, to: destinationFileUrl)
print("File was replaced")
print(NSArray(contentsOf: tempLocalUrl))
//print(tempLocalUrl)
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
}
task.resume()
I originally tried to overwrite the file that is bundled with the app to being with, that resulted in errors. So I instead tried to just save it in the app's documents folder and that removed that error. I had to make sure and remove any previous version of the file because it was giving me a file already exists error after the first run.
While it says everything is working (The outputs for both successful download and replaced file happen) when I print the contents of the array from the downloaded URL it just gives me nil.
This is my first attempt to use any kind of external resources in an app. Before I have always kept everything internal, so I am sure there is something glaringly obvious I am missing.
Update 1:
I realized I didn't have the correct URL to use to download a file from a Google drive. That line of code has been changed to:
let fileURL = URL(string: "https://drive.google.com/uc?export=download&id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
So now I actually am downloading the plist like I originally thought I was. Even removing the deletion issue mentioned in the first comment, I still can't get the downloaded file to actually replace the existing one.
Update 2:
I have reduced the actual file manipulation down to the following:
do {
try FileManager.default.replaceItemAt(destinationFileUrl, withItemAt: tempLocalUrl)
print("File was replaced")
print(NSArray(contentsOf: destinationFileUrl))
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
After the replacement is performed the output of the file shows the correct new contents that were downloaded from the internet.
Later in the code when I try and access the file it seems to be nil in content again.
Look at your download completion code. You:
Delete the file at the destination URL (in case there was one
leftover)
MOVE the temp file to the destination URL (removing it from the temp
URL)
Try to load the file from the temp URL.
What's wrong with this picture?
You are trying to get the contents of the moved file. You already moved the file to destination url and then you are trying to get the contents of the file from temporary location.
For getting file data, Please try the following :
let fileData = try! String(contentsOf: destinationFileUrl, encoding: String.Encoding.utf8)
print(fileData)

Unable save large file to S3 using Parse server

I tried to save to S3 bucket using Parse Server, and it can be saved correctly when the file is small, such as 864.2KB. However, when the file is large, say 5MB, it complaints with a message saying: "The data couldn’t be read because it isn’t in the correct format"
I'm using the following code to save the the video file to the S3
func saveVideo(withVideoURL url: URL){
let post = PFObject(className: "Post")
post["caption"] = "Out of the game for 6 months, but back with vengeance. Meet your 2017 AO Men's champion"
do{
let data = try Data(contentsOf: url)
print(data)
post["media"] = PFFile(data: data)
post.saveInBackground { (success, error) in
if success{
print("video saved")
}else{
print("failed")
if error != nil{
print(error!.localizedDescription)
}else{
print("erorr is nil")
}
}
}
}catch let error as NSError{
print("can't read")
print(error.localizedDescription)
}
}
Besides, even when the small video file is indeed being saved to the S3, it contains an extension .bin instead of, for example .mp4. I wonder what's happening here
The url end up looking something like this
https://s3-us-west-1.amazonaws.com/sampleApp/19d5bce20f8b55te1b1b8f370212533e5_file.bin
You need to stipulate the content type. You can do so like this:
post["media"] = PFFile(data: data, contentType: "video/mp4")
The below settings in your parse-server index file will help you:
var bodyParser = require('body-parser');
app.use(bodyParser.json({limit: '20mb'}));
app.use(bodyParser.urlencoded({limit: '20mb', extended: true}));
If you are using elastic beanstalk, you have to have a file named files.config inside the folder .ebextensions, with the below content.
files:
/etc/nginx/conf.d/proxy.conf:
content: |
client_max_body_size 20M;
This fixed the issue for me.

Resources