Retrieving a video from Documents Directory, but app cannot find location - ios

I have implemented Core Data to save string formatted URLS in my application. These URLS are URLS of videos they have recorded.
I used core data because I want the video to still be available to them after they exit out of the app. I am able to save and retrieve the URLS. However, when i use them to get the video thumbnails it does not work.
Here is where I declare the video file location:
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let cropUniqueId = NSUUID().uuidString
let outputPath = "\(documentsPath)/\(cropUniqueId).mov"
Then i convert it to a string and save the data to core data:
arrayOfStringPaths.append(outputPath)
stringOfArrayPaths = stringOfArrayPaths + arrayOfStringPaths.joined(separator: ",")
saveData(arrayPath: stringOfArrayPaths)
func saveData(arrayPath: String) {
let savedVideo = VideoPath(context: context)
savedVideo.fileLocations = arrayPath
appDelegate.saveContext()
print("Saved")
}
Everything so far works fine. It saves the URLS just as they are, i checked them with various print statements.
Now I retrieve the information when the user opens the app.
var data = [VideoPath]()
func fetchSavedData() {
do {
data = try context.fetch(VideoPath.fetchRequest())
for each in data {
// I append each url to the array.
videosArray.append(URL(fileURLWithPath: each.fileLocations!))
// They all print out correctly
print(each.fileLocations!)
}
for video in videosArray {
print("This is in video array")
// This prints out correctly as the URL i recorded earlier
print(video)
// This is where everything messes up
let thumbnail = getThumbnail(video)
thumbnails.append(thumbnail)
}
} catch {
print("There was an error")
}
}
When i try to get the thumbnail of the video it gives me this error here:
"The requested URL was not found on this server." UserInfo={NSLocalizedDescription=The requested URL was not found on this server., NSUnderlyingError=0x17064c330 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-802.0.53/src/swift/stdlib/public/core/ErrorType.swift

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:

Saving CloudKit Record to Local File Saves all fields Except CKAsset

I am trying to save an array of CKRecords to the documents directory in
order to have fast startup and offline access.
Downloading the CKRecords from CloudKit works fine and I am able to use the CKAsset in each record without issue. However, when I save the array of CKRecords that I downloaded to a local file, the CKAsset is not included in the data file. I can tell this from the size of the file saved to the documents directory. If I reconstitute the disk file into an array of CKRecords, I can retrieve all of the fields except the CKAsset. Other than the system fields, and the CKAsset field, all of the fields are Strings.
For testing - I have 10 CloudKit records each with six small String fields
and a CKAsset which is about 500KB. When I check the size of the
resulting file in documents the file size is about 15KB.
Here's the function to save the array. AppDelegate.ckStyleRecords is a
static array of the downloaded CKRecords.
func saveCKStyleRecordsToDisk() {
if AppDelegate.ckStyleRecords.count != 0 {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docsDirectoryURL = urls[0]
let ckStyleURL = docsDirectoryURL.appendingPathComponent("ckstylerecords.data")
do {
let data : Data = try NSKeyedArchiver.archivedData(withRootObject: AppDelegate.ckStyleRecords, requiringSecureCoding: true)
try data.write(to: ckStyleURL, options: .atomic)
print("data write ckStyleRecords successful")
} catch {
print("could not save ckStyleRecords to documents directory")
}
}//if count not 0
}//saveCKStyleRecordsToDisk
Here is the function to reconstitute the array.
func checkForExistenceOfCKStyleRecordsInDocuments(completion: #escaping ([CKRecord]) -> Void) {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docsDirectoryURL = urls[0]
let ckStyleURL = docsDirectoryURL.appendingPathComponent("ckstylerecords.data")
var newRecords : [CKRecord] = []
if FileManager.default.fileExists(atPath: ckStyleURL.path) {
do {
let data = try Data(contentsOf:ckStyleURL)
//yes, I know this has been deprecated, but I can't seem to get the new format to work
if let theRecords: [CKRecord] = try NSKeyedUnarchiver.unarchiveObject(with: data) as? [CKRecord] {
newRecords = theRecords
print("newRecords.count is \(newRecords.count)")
}
} catch {
print("could not retrieve ckStyleRecords from documents directory")
}
}//if exists
completion(newRecords)
}//checkForExistenceOfckStyleRecordsInDocuments
Calling the above:
kAppDelegate.checkForExistenceOfCKStyleRecordsInDocuments { (records) in
print("in button press and records.count is \(records.count)")
//this is just for test
for record in records {
print(record.recordID.recordName)
}
AppDelegate.ckStyleRecords = records
}//completion block
Upon refreshing the tableView that uses the ckStyleRecords array, all data
seems correct except the CKAsset (which in this case is a SceneKit
scene) is of course missing.
Any guidance would be appreciated.
A CKAsset was just a file reference. the fileURL property of the CKAsset is where the actual file is located. If you save a SKAsset then you only save the reference to the file. When doing that you do have to remember that this url is on a cache location which could be cleared if you are low on space.
You could do 2 things.
1. when reading your backup CKAsset, then also check if the file is located at the fileURL location. If the file is not there, then read it again from CloudKit.
2. Also backup the file from the fileURl to your documents folder. When you read your CKAsset from your backup, then just don't read the file from fileURL but the location where you have put it in your documents filter.

How to store and view the file in Iphone using IOS Swift

I am new to swift and trying to save the file on iphone and view them using file manager app present in app store. but every time the path looks like its getting stored in my mac machine. below is code which i have written for storing a simple text file
func saveImageDocumentDirectory(){
let str = "Super long string here"
let filename = getDocumentsDirectory().appendingPathComponent("output.txt")
do {
try str.write(to: filename, atomically: true, encoding: String.Encoding.utf8)
print(filename.path)
} catch {
// failed to write file – bad permissions, bad filename, missing permissions, or more likely it can't be converted to the encoding
}
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
but path at which file is getting stored is printed as below
/var/mobile/Containers/Data/Application/ACBC0B24-XXXX-XXXX-XXXX-BDAA4901EA41/Documents/output.txt

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)

Resources