Display and open saved files on TableView - ios

I made an pdf creator app. After creating pdf file I want to display the created files on a tableview. I created Tableview Controller and DocumentDirectoryTableTableViewController. I don't know how to do it after all of that.
I found this and this questions but I couldn't implement it into my code.
Also better to add my code for saving file:
// 5. Save PDF file
let path = "/MyApp/PdfFiles/MyPDF.pdf"
pdfData.write(toFile: path, atomically: true)
print("open \(path)") // command to open the generated file
UPDATE:
I can also get the App Directory and use it by this function:
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
and
let path = "\(getDocumentsDirectory())MyApp.pdf"

If you have NSData for the pdf file you're trying to store and if you want user to access it, you can write it using this:
func writePDF(data: NSData, to Name:String) -> Bool {
let pdfData:NSData = data
let pdfPaths:[String] = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
var pdfPath = pdfPaths[0]
pdfPath.append("_\(Name).pdf")
let result:Bool = pdfData.write(toFile: pdfPath, atomically: true)
return result
}
If you want to read pdf file programatically in your app given that you store it in the above path. It can be done using this:
func readPDF(Name: String) {
let pdfPaths:[String] = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
var pdfPath = pdfPaths[0]
pdfPath.append("_\(Name).pdf")
if let pdfData:NSData = NSData(contentsOfFile: pdfPath) { // verifying pdf exists in this path
let documentController: UIDocumentInteractionController = UIDocumentInteractionController(url: URL(fileURLWithPath: pdfPath))
documentController.delegate = self
documentController.presentPreview(animated: true)
}
}
// the below delegate function for `UIDocumentInteractionController` is necessary for it to present in the current `UIViewController`
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return self
}

Related

UIDocumentInteractionController change file name for Sharing

iam using the following code to show the sharing options for PDF
self.documentController = UIDocumentInteractionController(url: url)
self.documentController.name = "Test name" // not working
self.documentController.presentOptionsMenu(from: self.shareButton, animated: true)
the problem is that I save the PDF file name with datestamp to avoid having two files with the same name, but when the share options is being shown the actual file name appears,
is there is a way to show custom name instead of the actual filename (I don't want to copy the file to other place and rename it, waste of time and performance)
In such a situation, we can create a temporary folder which can contain the same file with lastPathExtension will be document.fileExtension and we can pass this newly file path to UIDocumentInteractionController.init(url: newFileUrl)
For Example:
func openUnsupportedFileWithPath(documentName : String, fileurl : URL, fileExtension : String, aDocument: SILDocumentDB? = nil, sourceView: UIView? = nil) -> Void {
// Create new temporary path
let paths: String = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
var newFileUrl: String = paths.appending("/Downloads/TemporaryFolder)")
newFileUrl = newFileUrl.appendingFormat("%#","\(documentName)")
let destinationPathUrl : URL
do {
// Move newly filePath with new fileName and fileExtension
destinationPathUrl = URL(fileURLWithPath: destinanewFileUrltionPath)
try FileManager.default.moveItem(at: fileurl, to: destinationPathUrl)
} catch {
print(error)
}
//Pass newly filePath to UIDocumentInteractionController
documentInteractionController = UIDocumentInteractionController.init(url: newFileUrl)
documentInteractionController?.name = documentName
documentInteractionController?.delegate = self
let canPreview = documentInteractionController?.presentPreview(animated: true)
if (canPreview == false) {
let activityViewController = UIActivityViewController.init(activityItems: [fileurl], applicationActivities: nil)
activityViewController.setValue(documentName, forKey: "subject")
if ISIPAD {
activityViewController.popoverPresentationController?.sourceView = sourceView ?? self.view
}
self.present(activityViewController, animated: true, completion: nil)
}
}
And UIDocumentInteractionController get dismiss, remove the temporary filePath on documentInteractionControllerDidEndPreview(_ controller: UIDocumentInteractionController) method.
public func documentInteractionControllerDidEndPreview(_ controller: UIDocumentInteractionController) {
documentInteractionController = nil
let paths: String = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let filePath: String = paths.appending("/Downloads/TemporaryFolder)")
let _fileManager : FileManager = FileManager.default
if filePath.length > 0 {
if _fileManager.fileExists(atPath: filePath) {
do{
try _fileManager.removeItem(atPath: filePath)
}catch let error as NSError{
print("\(error.localizedDescription)")
}
}
}
}

Store file at public space and easily accesed by any file explorer in swift iOS

I am trying to download one text file from the server and I have completed it.
Also, I have stored that file but it seems like it stores at some private location and not able to access that file from other file explorer.
Heres the code for it:
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
// let destinationUrl = documentsUrl!.appendingPathComponent("10xFile.pdf")
let fileManager = FileManager.default
do {
let documentDirectory = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let fileURL = documentDirectory.appendingPathComponent("10xFile.pdf")
let dataFromURL = NSData(contentsOf: location)
dataFromURL?.write(to: fileURL as! URL, atomically: true)
} catch {
print(error)
}
But I need to access this file very easily by any other file explore.
Thanks in advance
Actually as I commented - the feature of iOS - every app has completely separated filesystem sandbox, and one app can't access files of any another app without special permission created by another app.
But you can use UIActivityViewController to pass your pdf files to another app that can render your pdfs.
You can launch UIActivityViewController and pass your pdf file to it. In UIActivityViewController you can choose options - copy file or open in any of apps that can handle pdf file, for example FileManager app.
Here is the code sample somewhere based on code you provided in question, I created UIButton and added IBAction for it - UIActivityViewController is presented when user tap button.
override func viewDidLoad() {
super.viewDidLoad()
do {
let documentDirectory = getDocumentsDirectory()
let fileURL = documentDirectory.appendingPathComponent("10xFile.pdf")
let dataFromURL = try Data(contentsOf: location!)
try dataFromURL.write(to: fileURL, options: [])
} catch {
print(error)
}
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
#IBAction func actionButtonPressed(_ sender: Any) {
let documentsDir = getDocumentsDirectory()
let fileURL = documentsDir.appendingPathComponent("10xFile.pdf")
let ac = UIActivityViewController(activityItems: [fileURL], applicationActivities: nil)
self.present(ac, animated: true)
}

appendingPathComponent' is unavailable: Use appendingPathComponent on URL instead.a

I am using Swift 4, Xcode 9, and development target iOS 11.0.
I am trying to append a custom folder (MyFolder) to the path variable.
let outputFilePath = (NSTemporaryDirectory() as NSString).appending("MyFolder").appendingPathComponent((outputFileName as NSString).appendingPathExtension("mov")!)
But builder is giving error message:
appendingPathComponent' is unavailable: Use appendingPathComponent on URL instead.
I know, I am doing some silly mistake. Can you kindly help me in this?
Use this line
URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("MyFolder").appendingPathComponent(outputFileName).appendingPathExtension("mov")
instead of
(NSTemporaryDirectory() as NSString).appending("MyFolder").appendingPathComponent((outputFileName as NSString).appendingPathExtension("mov")!)
This will return you a url and use url.Path to get its path in string .
Hope this helps you.
Check below code for reference in document Directory
class func getDocumentsDirectory() -> URL {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let dataPath = documentsDirectory.appendingPathComponent("FolderName")
do {
try FileManager.default.createDirectory(atPath: dataPath.path, withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
print("Error creating directory: \(error.localizedDescription)")
}
return dataPath
}
For Appending Files in Folder You can use this
//name for file to be added
let uuid = UUID().uuidString
// storing a Audio File in Directory
let audioFilename = getDocumentsDirectory().appendingPathComponent("\(uuid).m4a")
To get Names of Files Available in the respected Folder created
//This function returns a Array with file names Available
class func getListOfRecordingsAvailable() -> [String] {
var fileNameArray = [String]()
let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let myFilesPath = documentDirectoryPath.appending("/FolderName")
let files = FileManager.default.enumerator(atPath: myFilesPath)
while let file = files?.nextObject() {
//myfilesPath - Path
//file - fileName
fileNameArray.append(file as! String)
}
print(fileNameArray)
return fileNameArray
}

Trying to delete files from documents directory

I have an app that allows users to record multiple videos and then post them to view later from a database. After they post the video, I want the app to delete the video from the documents directory to save phone storage. I am trying this, but when i check my phone's storage nothing is updated. Here is what I am doing
This is where i write the video to:
func videoFileLocation() -> String {
let uniqueID = NSUUID().uuidString
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let outputPath = "\(documentsPath)/\(uniqueID).mov"
return outputPath
}
This is how I remove them:
func clearDirectory() {
do {
data = try context.fetch(VideoPath.fetchRequest())
for each in data {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let path = "\(documentsPath)/\(each.fileLocations!)"
let url = URL(fileURLWithPath: path)
do {
try FileManager.default.removeItem(at: url)
print("Successfully Cleared")
} catch {
print("There was a problem removing the file")
}
}
The successfully cleared method gets printed out, but I see no change in my phone's storage. What am i doing wrong?
Replace
let path = "\(documentsPath)/\(each.fileLocations!)"
with below and try.
var path: String = nil
if let fileLocationPath = each.fileLocations as? String {
path = documentsPath.appendingPathComponent(fileLocationPath)
}

How to find NSDocumentDirectory in Swift?

I'm trying to get path to Documents folder with code:
var documentsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory:0,NSSearchPathDomainMask:0,true)
but Xcode gives error: Cannot convert expression's type 'AnyObject[]!' to type 'NSSearchPathDirectory'
I'm trying to understand what is wrong in the code.
Apparently, the compiler thinks NSSearchPathDirectory:0 is an array, and of course it expects the type NSSearchPathDirectory instead. Certainly not a helpful error message.
But as to the reasons:
First, you are confusing the argument names and types. Take a look at the function definition:
func NSSearchPathForDirectoriesInDomains(
directory: NSSearchPathDirectory,
domainMask: NSSearchPathDomainMask,
expandTilde: Bool) -> AnyObject[]!
directory and domainMask are the names, you are using the types, but you should leave them out for functions anyway. They are used primarily in methods.
Also, Swift is strongly typed, so you shouldn't just use 0. Use the enum's value instead.
And finally, it returns an array, not just a single path.
So that leaves us with (updated for Swift 2.0):
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
and for Swift 3:
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
Swift 3.0 and 4.0
Directly getting first element from an array will potentially cause exception if the path is not found. So calling first and then unwrap is the better solution
if let documentsPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
//This gives you the string formed path
}
if let documentsPathURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
//This gives you the URL of the path
}
The modern recommendation is to use NSURLs for files and directories instead of NSString based paths:
So to get the Document directory for the app as an NSURL:
func databaseURL() -> NSURL? {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
if let documentDirectory: NSURL = urls.first as? NSURL {
// This is where the database should be in the documents directory
let finalDatabaseURL = documentDirectory.URLByAppendingPathComponent("items.db")
if finalDatabaseURL.checkResourceIsReachableAndReturnError(nil) {
// The file already exists, so just return the URL
return finalDatabaseURL
} else {
// Copy the initial file from the application bundle to the documents directory
if let bundleURL = NSBundle.mainBundle().URLForResource("items", withExtension: "db") {
let success = fileManager.copyItemAtURL(bundleURL, toURL: finalDatabaseURL, error: nil)
if success {
return finalDatabaseURL
} else {
println("Couldn't copy file to final location!")
}
} else {
println("Couldn't find initial database in the bundle!")
}
}
} else {
println("Couldn't get documents directory!")
}
return nil
}
This has rudimentary error handling, as that sort of depends on what your application will do in such cases. But this uses file URLs and a more modern api to return the database URL, copying the initial version out of the bundle if it does not already exist, or a nil in case of error.
Xcode 8.2.1 • Swift 3.0.2
let documentDirectoryURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
Xcode 7.1.1 • Swift 2.1
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
Usually I prefer to use this extension:
Swift 3.x and Swift 4.0:
extension FileManager {
class func documentsDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as [String]
return paths[0]
}
class func cachesDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true) as [String]
return paths[0]
}
}
Swift 2.x:
extension NSFileManager {
class func documentsDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as [String]
return paths[0]
}
class func cachesDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true) as [String]
return paths[0]
}
}
More convenient Swift 3 method:
let documentsUrl = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first!
For everyone who looks example that works with Swift 2.2, Abizern code with modern do try catch handle of error
func databaseURL() -> NSURL? {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
if let documentDirectory:NSURL = urls.first { // No use of as? NSURL because let urls returns array of NSURL
// This is where the database should be in the documents directory
let finalDatabaseURL = documentDirectory.URLByAppendingPathComponent("OurFile.plist")
if finalDatabaseURL.checkResourceIsReachableAndReturnError(nil) {
// The file already exists, so just return the URL
return finalDatabaseURL
} else {
// Copy the initial file from the application bundle to the documents directory
if let bundleURL = NSBundle.mainBundle().URLForResource("OurFile", withExtension: "plist") {
do {
try fileManager.copyItemAtURL(bundleURL, toURL: finalDatabaseURL)
} catch let error as NSError {// Handle the error
print("Couldn't copy file to final location! Error:\(error.localisedDescription)")
}
} else {
print("Couldn't find initial database in the bundle!")
}
}
} else {
print("Couldn't get documents directory!")
}
return nil
}
Update
I've missed that new swift 2.0 have guard(Ruby unless analog), so with guard it is much shorter and more readable
func databaseURL() -> NSURL? {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
// If array of path is empty the document folder not found
guard urls.count != 0 else {
return nil
}
let finalDatabaseURL = urls.first!.URLByAppendingPathComponent("OurFile.plist")
// Check if file reachable, and if reacheble just return path
guard finalDatabaseURL.checkResourceIsReachableAndReturnError(nil) else {
// Check if file is exists in bundle folder
if let bundleURL = NSBundle.mainBundle().URLForResource("OurFile", withExtension: "plist") {
// if exist we will copy it
do {
try fileManager.copyItemAtURL(bundleURL, toURL: finalDatabaseURL)
} catch let error as NSError { // Handle the error
print("File copy failed! Error:\(error.localizedDescription)")
}
} else {
print("Our file not exist in bundle folder")
return nil
}
return finalDatabaseURL
}
return finalDatabaseURL
}
Xcode 8b4 Swift 3.0
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
Usually i prefer like below in swift 3, because i can add file name and create a file easily
let fileManager = FileManager.default
if let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
let databasePath = documentsURL.appendingPathComponent("db.sqlite3").path
print("directory path:", documentsURL.path)
print("database path:", databasePath)
if !fileManager.fileExists(atPath: databasePath) {
fileManager.createFile(atPath: databasePath, contents: nil, attributes: nil)
}
}
Copy and paste this line in App delegate like this and it will print path like this
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
print(NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last! as String)
return true
}
Copy the path and paste it in go To Folder in finder by right clicking on it then enter
Open the file in Xcode

Resources