I enabled Document Types to import or copy files from other apps to my application. I have some questions :
1- Where should create the method of moving files form Inbox to Document directory ? is this the right place ?
func applicationWillEnterForeground(_ application: UIApplication)
2- On first view controller I am getting files from Document directory :
func getFileListByDate() -> [String]? {
let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
if let urlArray = try? FileManager.default.contentsOfDirectory(at: directory,
includingPropertiesForKeys: [.contentModificationDateKey],
options:.skipsHiddenFiles) {
return urlArray.map { url in
(url.lastPathComponent, (try? url.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast)
}
.sorted(by: { $0.1 > $1.1 }) // sort descending modification dates
.map { $0.0 } // extract file names
} else {
return nil
}
}
But when a file imports to my app there is Inbox folder(item) in my table view , how can I automatically move files from Inbox to Document directory and remove Inbox folder ?
If your app needs to open a file coming from another App you need to implement delegate method
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
and move the url to the folder of your choice inside your App.
let url = url.standardizedFileURL // this will strip out the private from your url
// if you need to know which app is sending the file or decide if you will open in place or not you need to check the options
let openInPlace = options[.openInPlace] as? Bool == true
let sourceApplication = options[.sourceApplication] as? String
let annotation = options[.annotation] as? [String: Any]
// checking the options info
print("openInPlace:", openInPlace)
print("sourceApplication:", sourceApplication ?? "")
print("annotation:", annotation ?? "")
Moving the file out of the inbox to your destination URL in your case the documents directory appending the url.lastPathComponent:
do {
try FileManager.default.moveItem(at: url, to: destinationURL)
print(url.path)
print("file moved from:", url, "to:", destinationURL)
} catch {
print(error)
return false
}
return true
Related
I have an XML file in my Xcode Project, and I'm trying to first save it to disk, and secondly how can I tell if I've successfully saved it? Is this the proper approach? Using the simulator I navigated to the new "Files" folder in iOS 11 and I don't see it but I'm not sure if it should be there or not?
guard let path = Bundle.main.url(forResource: "sample", withExtension: "xml") else {print("NO URL"); return}
let sample = try? Data(contentsOf: path)
print("sample XML = \(String(describing: sample?.debugDescription))")
//put xml file on the device
let filename = getDocumentsDirectory().appendingPathComponent("sample.xml")
do {
try sample?.write(to: filename)
} catch {
print("ERROR")
}
updated to include my check if file exists:
//check if file exists
let checkPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: checkPath)
let filePath = url.appendingPathComponent("sample.xml").path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
print("FILE AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
You can use UIDocumentInteractionController and let the user select where he wants to save your file when you share your url. The user just needs to select save to files and choose which directory to save the file you are exporting.
You can use UIDocumentInteractionController to share any file type located inside your App bundle, at your Documents directory or another folder accessible from your App.
class ViewController: UIViewController {
let documentInteractionController = UIDocumentInteractionController()
func share(url: URL) {
documentInteractionController.url = url
documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = url.localizedName ?? url.lastPathComponent
documentInteractionController.presentOptionsMenu(from: view.frame, in: view, animated: true)
}
#IBAction func shareAction(_ sender: UIButton) {
guard let url = URL(string: "https://www.ibm.com/support/knowledgecenter/SVU13_7.2.1/com.ibm.ismsaas.doc/reference/AssetsImportCompleteSample.csv?view=kc") else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
let tmpURL = FileManager.default.temporaryDirectory
.appendingPathComponent(response?.suggestedFilename ?? "fileName.csv")
do {
try data.write(to: tmpURL)
DispatchQueue.main.async {
self.share(url: tmpURL)
}
} catch {
print(error)
}
}.resume()
}
}
extension URL {
var typeIdentifier: String? {
return (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier
}
var localizedName: String? {
return (try? resourceValues(forKeys: [.localizedNameKey]))?.localizedName
}
}
edit/update:
If you would like to expose the files located on your App Bundle's document directory you can check this post How can I display my App documents in the Files app for iPhone
Add these two keys to 'info.plist' -
...
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
...
After this, files saved in '.documentDirectory' will appear in 'Files App' inside a folder with your app name.
If editing 'info.plist' in Xcode then add below keys -
Application supports iTunes file sharing = YES
Supports opening documents in place = YES
info.plist
I have an XML file in my Xcode Project, and I'm trying to first save it to disk, and secondly how can I tell if I've successfully saved it? Is this the proper approach? Using the simulator I navigated to the new "Files" folder in iOS 11 and I don't see it but I'm not sure if it should be there or not?
guard let path = Bundle.main.url(forResource: "sample", withExtension: "xml") else {print("NO URL"); return}
let sample = try? Data(contentsOf: path)
print("sample XML = \(String(describing: sample?.debugDescription))")
//put xml file on the device
let filename = getDocumentsDirectory().appendingPathComponent("sample.xml")
do {
try sample?.write(to: filename)
} catch {
print("ERROR")
}
updated to include my check if file exists:
//check if file exists
let checkPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: checkPath)
let filePath = url.appendingPathComponent("sample.xml").path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
print("FILE AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
You can use UIDocumentInteractionController and let the user select where he wants to save your file when you share your url. The user just needs to select save to files and choose which directory to save the file you are exporting.
You can use UIDocumentInteractionController to share any file type located inside your App bundle, at your Documents directory or another folder accessible from your App.
class ViewController: UIViewController {
let documentInteractionController = UIDocumentInteractionController()
func share(url: URL) {
documentInteractionController.url = url
documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = url.localizedName ?? url.lastPathComponent
documentInteractionController.presentOptionsMenu(from: view.frame, in: view, animated: true)
}
#IBAction func shareAction(_ sender: UIButton) {
guard let url = URL(string: "https://www.ibm.com/support/knowledgecenter/SVU13_7.2.1/com.ibm.ismsaas.doc/reference/AssetsImportCompleteSample.csv?view=kc") else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
let tmpURL = FileManager.default.temporaryDirectory
.appendingPathComponent(response?.suggestedFilename ?? "fileName.csv")
do {
try data.write(to: tmpURL)
DispatchQueue.main.async {
self.share(url: tmpURL)
}
} catch {
print(error)
}
}.resume()
}
}
extension URL {
var typeIdentifier: String? {
return (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier
}
var localizedName: String? {
return (try? resourceValues(forKeys: [.localizedNameKey]))?.localizedName
}
}
edit/update:
If you would like to expose the files located on your App Bundle's document directory you can check this post How can I display my App documents in the Files app for iPhone
Add these two keys to 'info.plist' -
...
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
...
After this, files saved in '.documentDirectory' will appear in 'Files App' inside a folder with your app name.
If editing 'info.plist' in Xcode then add below keys -
Application supports iTunes file sharing = YES
Supports opening documents in place = YES
info.plist
I have an XML file in my Xcode Project, and I'm trying to first save it to disk, and secondly how can I tell if I've successfully saved it? Is this the proper approach? Using the simulator I navigated to the new "Files" folder in iOS 11 and I don't see it but I'm not sure if it should be there or not?
guard let path = Bundle.main.url(forResource: "sample", withExtension: "xml") else {print("NO URL"); return}
let sample = try? Data(contentsOf: path)
print("sample XML = \(String(describing: sample?.debugDescription))")
//put xml file on the device
let filename = getDocumentsDirectory().appendingPathComponent("sample.xml")
do {
try sample?.write(to: filename)
} catch {
print("ERROR")
}
updated to include my check if file exists:
//check if file exists
let checkPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: checkPath)
let filePath = url.appendingPathComponent("sample.xml").path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
print("FILE AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
You can use UIDocumentInteractionController and let the user select where he wants to save your file when you share your url. The user just needs to select save to files and choose which directory to save the file you are exporting.
You can use UIDocumentInteractionController to share any file type located inside your App bundle, at your Documents directory or another folder accessible from your App.
class ViewController: UIViewController {
let documentInteractionController = UIDocumentInteractionController()
func share(url: URL) {
documentInteractionController.url = url
documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = url.localizedName ?? url.lastPathComponent
documentInteractionController.presentOptionsMenu(from: view.frame, in: view, animated: true)
}
#IBAction func shareAction(_ sender: UIButton) {
guard let url = URL(string: "https://www.ibm.com/support/knowledgecenter/SVU13_7.2.1/com.ibm.ismsaas.doc/reference/AssetsImportCompleteSample.csv?view=kc") else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
let tmpURL = FileManager.default.temporaryDirectory
.appendingPathComponent(response?.suggestedFilename ?? "fileName.csv")
do {
try data.write(to: tmpURL)
DispatchQueue.main.async {
self.share(url: tmpURL)
}
} catch {
print(error)
}
}.resume()
}
}
extension URL {
var typeIdentifier: String? {
return (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier
}
var localizedName: String? {
return (try? resourceValues(forKeys: [.localizedNameKey]))?.localizedName
}
}
edit/update:
If you would like to expose the files located on your App Bundle's document directory you can check this post How can I display my App documents in the Files app for iPhone
Add these two keys to 'info.plist' -
...
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
...
After this, files saved in '.documentDirectory' will appear in 'Files App' inside a folder with your app name.
If editing 'info.plist' in Xcode then add below keys -
Application supports iTunes file sharing = YES
Supports opening documents in place = YES
info.plist
So, I'm making an importing system to bring text files from email into the app to read there contents. I am very new to swift, and app programming at that (mainly do backend), and I am having an issue with the code below. It is most likely very inefficient and there is probably a better way to do this, but currently I have the func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool with some other code to assign variables to the URL to send to the view controller (haven't worked with notifications/rootviewcontrollers yet). however, after running this code, the result, instead of the contents of the file, is ("matrixFile4197009889-26.text", Unicode (UTF-8)). What should I do? Please explain in "baby language."
My view controller code:
let delegate = UIApplication.shared.delegate as! AppDelegate
if delegate.importFileIndicator == true {
let filemgr = FileManager.default
let docsDirURL = try! filemgr.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let inboxURL = docsDirURL.appendingPathComponent("Inbox")
print(inboxURL)
do{
var directoryContents = try FileManager.default.contentsOfDirectory(at: inboxURL, includingPropertiesForKeys: nil, options: [])
var fileSearchBoolCounter = false
var fileSearchCounter = 0
var fileURL: URL
while fileSearchBoolCounter == false {
if (String(describing: directoryContents[fileSearchCounter].lastPathComponent).range(of: String(describing: NSURL(string: delegate.urlString)!.lastPathComponent!)) != nil) {
fileURL = directoryContents[fileSearchCounter]
fileSearchBoolCounter = true
print(fileURL)
let path = inboxURL.appendingPathComponent((NSURL(string: delegate.urlString)?.lastPathComponent!)!)
encryptedMessageField.text = try String(contentsOfFile: String(describing: path), encoding: String.Encoding.utf8)
}else{
print(directoryContents[fileSearchCounter])
fileSearchCounter += 1
print(NSURL(string: delegate.urlString)!.lastPathComponent!)
}
}
delegate.importFileIndicator = false
fileSearchBoolCounter = false
fileSearchCounter = 0
}catch let error as NSError{
print(error)
}
}
My AppDelegate code:
var importFileIndicator = false
var urlString = ""
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
urlString = String(describing: url)
print(urlString)
importFileIndicator = true
return true
}
I think you've already good with some parts, but I'm going to include them too in the whole process.
1. Make your app available to open a TXT file
To let the system know that your app is prepared to receive a TXT file, you need to configure the Info.plist itself, or the simplest way is to configure via TARGETS/"Info tab"/"Document Types section":
At this point your app becomes available to handle the TXT files coming from other external applications. So when you're about to open a TXT file that is attached to a mail, you should see your app available in the list:
2. Prepare app to receive incoming TXT file
In order to handle the supported file type, you need to implement the application:openURL:options: method you've already mentioned in your AppDelegate. Here you receive the file path as url, that you can easily send towards your ViewController for further processing. This url should looks something like this:
(lldb) po url
▿ file:///private/var/mobile/Containers/Data/Application/42D78E58-C7EC-4F3B-9100-B731AF7A4E45/Documents/Inbox/sample.txt
3. Handle the TXT file
Here you can also store the file's content in a String using the appropriate String initializer.
String(contentsOf: url, encoding: String.Encoding.utf8)
and then you can pass that String to your ViewController.
So your application:openURL:options: in your AppDelegate should looks something like this (depends on your actual view controller hierarchy) :
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
do {
let contentString = try String(contentsOf: url, encoding: .utf8)
if let window = self.window, let viewController = window.rootViewController as? ViewController {
viewController.displayText(text: contentString)
// here you pass the actual content as String to your custom ViewController which implements a displayText: function that receives a string
}
}
catch {
// contents could not be loaded
}
return true
}
If I attempt to remove a file using the FileManager class I get an error.
The function below returns true, which means that I need to set the delete privileges attributes. However I haven't found an example on how to do it.
func isDeletableFile(atPath path: String) -> Bool
Any help?
Code:
func fileManager(_ fileManager: FileManager, shouldRemoveItemAtPath path: String) -> Bool {
// print("Should remove invoked for path \(path)")
return true
}
func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, removingItemAt URL: URL) -> Bool {
//print("Should process")
return true
}
func deleteAllFiles(subPath : String) {
var url = Bundle.main.bundleURL
url = url.appendingPathComponent(subPath)
let fileManager = FileManager.default
fileManager.delegate = self
if let enumerator = fileManager.enumerator(at: url, includingPropertiesForKeys: nil) {
for file in enumerator {
let fileAsNSURL = file as! NSURL
print("")
print("Deleting: \(fileAsNSURL.absoluteString!)")
print("")
do {
// I would like to set the deletable permissions before checking this..
if (fileManager.isDeletableFile(atPath: fileAsNSURL.absoluteString!)){
try fileManager.removeItem(atPath: fileAsNSURL.absoluteString!)
}
else{
print("its not deletable")
}
}
catch let error {
print("file-delete-error:\n\(error) for path \(fileAsNSURL.absoluteString!)")
}
}
}
}
There is a common misunderstanding:
In the file system a you have to call path on the URL to get the path
fileManager.isDeletableFile(atPath: fileAsNSURL.path)
absoluteString returns the (percent escaped) string representation of the URL starting with the scheme (http://, file://)
For example you have an URL (don't use NSURL in Swift 3):
let url = URL(fileURLWithPath:"/Users/myUser/Application Support")
url.path returns "/Users/myUser/Application Support"
url.absoluteString returns "file:///Users/myUser/Application%20Support"