I've created a streaming video app that also downloads videos locally. How can I display downloaded videos in the iPhone/iPad Storage sections of settings?
I'm downloading using background tasks, and on complete running
let docsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationUrl = docsUrl.appendingPathComponent(videoId + ".mp4")
let fileManager = FileManager.default
try? fileManager.removeItem(at: destinationUrl)
do {
try fileManager.copyItem(at: location, to: destinationUrl)
} catch let error {
print("Could not copy file to disk: \(error.localizedDescription)")
return
}
Apps like Netflix, Disney+ and Prime Video all show the downloaded shows and allow them to be deleted individually, but I haven't been able to figure out how it's done. All searches usually lead to guides for users on how to delete videos.
Anybody have any tips?
This code is working for me:
func downloadVideo1(videoString :String)
{
let myStringArr = videoString.components(separatedBy: "/")
let finalString = myStringArr[myStringArr.count - 1]
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
let docsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationUrl = docsUrl.appendingPathComponent(finalString)
if(FileManager().fileExists(atPath: destinationUrl.path)){
print("\n\nfile already exists\n\n")
}
else{
// setupProgress()
//DispatchQueue.global(qos: .background).async {
var request = URLRequest(url: URL(string: videoString)!)
request.httpMethod = "GET"
_ = session.dataTask(with: request, completionHandler: { (data, response, error) in
if(error != nil){
print("\n\nsome error occured\n\n")
return
}
if let response = response as? HTTPURLResponse{
if response.statusCode == 200{
DispatchQueue.main.async {
if let data = data{
if let _ = try? data.write(to: destinationUrl, options: Data.WritingOptions.atomic){
print("\n\nurl data written\n\n")
print(destinationUrl)
self.checkCount = self.checkCount + 1
if self.checkCount == self.availableMediaCount
{
self.progressView.isHidden = true
let vc = TeacherLessonPlanSB.instantiateViewController(withIdentifier: "DownloadLessonVC") as! DownloadLessonViewController
self.navigationController?.pushViewController(vc, animated: true)
}
}
else{
print("\n\nerror again\n\n")
}
}//end if let data
}//end dispatch main
}//end if let response.status
}
}).resume()
}
}
I have a download function working perfectly, on click of my button, the file is being download correctly.
What I am trying to achieve, If I put the App in the background or change screen while it's downloading the file, I'd like the download to be continued. Currently if the App is in the background ,the download stop and user need to restart it from zero.
How is this possible to make ?? Below is my download function, I tried to add a background download without success.
func createDownloadTask() {
let downloadRequest = URLRequest(url: URL(string: "\(posts[selectedIndexPath].link)")!)
let session = Foundation.URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
downloadTask = session.downloadTask(with: downloadRequest)
downloadTask!.resume()
}
and
#IBAction func startDownload(_ sender: UIButton) {
let urlString = "\(posts[selectedIndexPath].link)"
DispatchQueue.global(qos: .default).async(execute: {
print("downloadVideo");
let url=NSURL(string: urlString);
let urlData=NSData(contentsOf: url! as URL);
if((urlData) != nil)
{
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let fileName = urlString as NSString;
let filePath="\(documentsPath)/\(fileName.lastPathComponent)";
DispatchQueue.main.async(execute: { () -> Void in
print(filePath)
urlData?.write(toFile: filePath, atomically: true);
print("video Saved to document directory of app");
self.downloadButton.alpha = 0;
self.progressView.alpha = 0;
self.cardboardButton.alpha = 1;
self.videoButton.alpha = 1;
self.removefile.alpha = 1;
})
}
})
}
You need to use UIBackgroundTaskIdentifier. You can start some task in background with beginBackgroundTask see:
https://developer.apple.com/reference/uikit/uiapplication/1623031-beginbackgroundtask.
System will give you some time and you can finish your task. If the time expire you will receive handler.See example that I use to upload a image
static var backgroundUpload:UIBackgroundTaskIdentifier?
static func praiseCollegue(praiseCollegueId:Int,image:UIImageView,praisedBy:Int,praiseText:String,praiseCategory:Int,successBlock: (()->())?,errorBlock: (()->())?){
backgroundUpload = UIApplication.shared.beginBackgroundTask(expirationHandler: {
if let task = self.backgroundUpload{
UIApplication.shared.endBackgroundTask(task)
}
})
let url = NSURL(string: "someUrl")
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "POST"
request.setValue(InfoManager.sharedInstance.tokenInfo?.accessToken, forHTTPHeaderField: "AccessToken")
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest) {
(
data, response, error) in
if let responseData = data{
do {
if let dict = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: AnyObject]{
if let errorCode = dict["errorCode"] as? Int{
if(errorCode == 1){
successBlock?()
if let task = self.backgroundUpload{
UIApplication.shared.endBackgroundTask(task)
}
}
else{
errorBlock?()
if let task = self.backgroundUpload{
UIApplication.shared.endBackgroundTask(task)
}
}
}
}
}
}
}
task.resume()
}
i am storing my data in file manager in my app. now i want to delete specific data by code so how can i do this?
here is my code which i used for store data
var localURL : String
init()
{
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
localURL = urls.first!.URLByAppendingPathComponent("podcasts").path!
createDirectory(localURL)
}
func downloadShow(slug: String, show: NSDictionary) {
SVProgressHUD.showWithStatus("Downloading...")
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
let url = NSURL(string: show["file"] as! String)
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "GET"
let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if (error == nil) {
let showFileName = url?.lastPathComponent
let programMP3Path = self.localURL + "/" + slug + "/" + showFileName!
let programDataPath = programMP3Path + ".dat"
data?.writeToFile(programMP3Path, atomically: true)
show.writeToFile(programDataPath, atomically: true)
print("Success")
print(showFileName)
SVProgressHUD.dismiss()
}
else {
// Failure
print("Faulure: \(error)");
}
})
task.resume()
}}
Not sure about session methods. Here's how to delete file in user temp directory, if that helps
let myFileName = "myFile.txt"
var fileManager = NSFileManager()
var tempDirectory = NSTemporaryDirectory()
let filePath = tempDirectory.stringByAppendingPathComponent(myFileName)
var error: NSError?
// also good idea to check before if the file is in the directory
let path = tmpDir.stringByAppendingPathComponent(isFileInDir)
fileManager.removeItemAtPath(path, error: &error)
I want to build an app which also includes the possibility to show and save PDFs inside the app and display them (as a FileSystem) within a tableview and open them when I tap on one PDF.
Here are my important questions for that:
1. How do I save a PDF local on my app ( for example if the user can enter a url) and where exactly will it save it ?
2. When saved, how can I show all the local storaged files within a tableview to open them?
Since several people requested this, here is the equivalent to the first answer in Swift:
//The URL to Save
let yourURL = NSURL(string: "http://somewebsite.com/somefile.pdf")
//Create a URL request
let urlRequest = NSURLRequest(URL: yourURL!)
//get the data
let theData = NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse: nil, error: nil)
//Get the local docs directory and append your local filename.
var docURL = (NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)).last as? NSURL
docURL = docURL?.URLByAppendingPathComponent( "myFileName.pdf")
//Lastly, write your file to the disk.
theData?.writeToURL(docURL!, atomically: true)
Also, since this code uses a synchronous network request, I highly recommend dispatching it to a background queue:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
//The URL to Save
let yourURL = NSURL(string: "http://somewebsite.com/somefile.pdf")
//Create a URL request
let urlRequest = NSURLRequest(URL: yourURL!)
//get the data
let theData = NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse: nil, error: nil)
//Get the local docs directory and append your local filename.
var docURL = (NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)).last as? NSURL
docURL = docURL?.URLByAppendingPathComponent( "myFileName.pdf")
//Lastly, write your file to the disk.
theData?.writeToURL(docURL!, atomically: true)
})
And the answer to second question in Swift:
//Getting a list of the docs directory
let docURL = (NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last) as? NSURL
//put the contents in an array.
var contents = (NSFileManager.defaultManager().contentsOfDirectoryAtURL(docURL!, includingPropertiesForKeys: nil, options: NSDirectoryEnumerationOptions.SkipsHiddenFiles, error: nil))
//print the file listing to the console
println(contents)
Swift 4.1
func savePdf(urlString:String, fileName:String) {
DispatchQueue.main.async {
let url = URL(string: urlString)
let pdfData = try? Data.init(contentsOf: url!)
let resourceDocPath = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last! as URL
let pdfNameFromUrl = "YourAppName-\(fileName).pdf"
let actualPath = resourceDocPath.appendingPathComponent(pdfNameFromUrl)
do {
try pdfData?.write(to: actualPath, options: .atomic)
print("pdf successfully saved!")
} catch {
print("Pdf could not be saved")
}
}
}
func showSavedPdf(url:String, fileName:String) {
if #available(iOS 10.0, *) {
do {
let docURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let contents = try FileManager.default.contentsOfDirectory(at: docURL, includingPropertiesForKeys: [.fileResourceTypeKey], options: .skipsHiddenFiles)
for url in contents {
if url.description.contains("\(fileName).pdf") {
// its your file! do what you want with it!
}
}
} catch {
print("could not locate pdf file !!!!!!!")
}
}
}
// check to avoid saving a file multiple times
func pdfFileAlreadySaved(url:String, fileName:String)-> Bool {
var status = false
if #available(iOS 10.0, *) {
do {
let docURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let contents = try FileManager.default.contentsOfDirectory(at: docURL, includingPropertiesForKeys: [.fileResourceTypeKey], options: .skipsHiddenFiles)
for url in contents {
if url.description.contains("YourAppName-\(fileName).pdf") {
status = true
}
}
} catch {
print("could not locate pdf file !!!!!!!")
}
}
return status
}
I am giving an example of storing and retrieving a pdf document in iOS. I hope that is what you are looking for.
1. How do I save a PDF local on my app ( for example if the user can enter a url) and where exactly will it save it ?
// the URL to save
NSURL *yourURL = [NSURL URLWithString:#"http://yourdomain.com/yourfile.pdf"];
// turn it into a request and use NSData to load its content
NSURLRequest *request = [NSURLRequest requestWithURL:result.link];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
// find Documents directory and append your local filename
NSURL *documentsURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
documentsURL = [documentsURL URLByAppendingPathComponent:#"localFile.pdf"];
// and finally save the file
[data writeToURL:documentsURL atomically:YES];
2. When saved, how can I show all the local storaged files within a tableview to open them?
You can check that the file has downloaded, or you can list the Documents directory like so:
// list contents of Documents Directory just to check
NSURL *documentsURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSArray *contents = [[NSFileManager defaultManager]contentsOfDirectoryAtURL:documentsURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles error:nil];
NSLog(#"%#", [contents description]);
Downloading and displaying PDF in Webview using Swift.
let request = URLRequest(url: URL(string: "http://<your pdf url>")!)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
if error == nil{
if let pdfData = data {
let pathURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("\(filename).pdf")
do {
try pdfData.write(to: pathURL, options: .atomic)
}catch{
print("Error while writting")
}
DispatchQueue.main.async {
self.webView.delegate = self
self.webView.scalesPageToFit = true
self.webView.loadRequest(URLRequest(url: pathURL))
}
}
}else{
print(error?.localizedDescription ?? "")
}
}); task.resume()
If you want to store file in Files app add`
NSURL *url = [NSURL URLWithString:#"PATH TO PDF"];
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithURL:url inMode:UIDocumentPickerModeExportToService];
[documentPicker setDelegate:self];
[self presentViewController:documentPicker animated:YES completion:nil];
And here are delegate methods
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
}
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
}
It will open a DocumentPickerViewController where you can choose a folder to store the file.
Requires iOS11 or later.
if you want to print the PDF data wich is in the directoryURL then use :
let printInfo = NSPrintInfo.shared
let manager = FileManager.default
do{
let directoryURL = try manager.url(for: .documentDirectory, in:.userDomainMask, appropriateFor:nil, create:true)
let docURL = NSURL(string:"LadetagMahlzeiten.pdf", relativeTo:directoryURL)
let pdfDoc = PDFDocument.init(url: docURL! as URL)
let page = CGRect(x: 0, y: 0, width: 595.2, height: 1841.8) // A4, 72 dpi
let pdfView : PDFView = PDFView.init(frame: page)
pdfView.document = pdfDoc
let operation: NSPrintOperation = NSPrintOperation(view: pdfView, printInfo: printInfo)
operation.printPanel.options.insert(NSPrintPanel.Options.showsPaperSize)
operation.printPanel.options.insert(NSPrintPanel.Options.showsOrientation)
operation.run()
}catch{
}
//savePdf(urlString:url, fileName:fileName)
let urlString = "here String with your URL"
let url = URL(string: urlString)
let fileName = String((url!.lastPathComponent)) as NSString
// Create destination URL
let documentsUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("\(fileName)")
//Create URL to the source file you want to download
let fileURL = URL(string: urlString)
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.copyItem(at: tempLocalUrl, to: destinationFileUrl)
do {
//Show UIActivityViewController to save the downloaded file
let contents = try FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
for indexx in 0..<contents.count {
if contents[indexx].lastPathComponent == destinationFileUrl.lastPathComponent {
let activityViewController = UIActivityViewController(activityItems: [contents[indexx]], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
}
}
}
catch (let err) {
print("error: \(err)")
}
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: \(error?.localizedDescription ?? "")")
}
}
task.resume()
}
For Swift 5 and up Version: Save PDF base64 String Data to Document Directory
Create a Folder where you want to save PDF file with Name
fileprivate func getFilePath() -> URL? {
let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let directoryURl = documentDirectoryURL.appendingPathComponent("Invoice", isDirectory: true)
if FileManager.default.fileExists(atPath: directoryURl.path) {
return directoryURl
} else {
do {
try FileManager.default.createDirectory(at: directoryURl, withIntermediateDirectories: true, attributes: nil)
return directoryURl
} catch {
print(error.localizedDescription)
return nil
}
}
}
Write PDF base64 String Data to Document Directory
fileprivate func saveInvoice(invoiceName: String, invoiceData: String) {
guard let directoryURl = getFilePath() else {
print("Invoice save error")
return }
let fileURL = directoryURl.appendingPathComponent("\(invoiceName).pdf")
guard let data = Data(base64Encoded: invoiceData, options: .ignoreUnknownCharacters) else {
print("Invoice downloaded Error")
self.hideHUD()
return
}
do {
try data.write(to: fileURL, options: .atomic)
print("Invoice downloaded successfully")
} catch {
print(error.localizedDescription)
}
}
I have allready implemented a download session, now what i wanna do is make it to download only a portion of that file. i know that its possible through the byte-range but im not sure how do i have to do that in swift. any help would be much appreciated. thanks.
#IBAction func btnStartDownload(sender: NSButton) {
let downloadUrl = NSURL(string:"http://www.joomlaworks.net/images/demos/galleries/abstract/7.jpg")
let sessionConfiguration:NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session:NSURLSession = NSURLSession(configuration: sessionConfiguration)
let sessionDownloadTask:NSURLSessionDownloadTask = session.downloadTaskWithURL(downloadUrl!, completionHandler: { (data, response, error) -> Void in
let data = NSData(contentsOfURL: data)
var fileManager:NSFileManager = NSFileManager.defaultManager()
var paths:NSArray = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
var documentsDirectory:NSString = paths[0] as NSString
var databasePath:NSString = documentsDirectory.stringByAppendingString("/\(response.suggestedFilename!)")
fileManager.createFileAtPath(databasePath, contents: data , attributes: nil)
NSLog("Database copied to\(databasePath)")
})
sessionDownloadTask.resume()
}