I've been trying to create a Today extension that needs access to a .plist file in the documents directory. I have setup the App group for both the app and the extension.
While I have seen examples for NSUserDefaults, I couldn't find anything for accessing files.
I tried accessing the file like this (which works in the app itself)
let paths = NSSearchPathForDirectoriesInDomains(
.DocumentDirectory, .UserDomainMask, true)
let documentsDirectory = paths[0] as String
let filePath = "\(documentsDirectory)/config.plist"
if NSFileManager.defaultManager().fileExistsAtPath(filePath) // false
{
…
}
But I get the following error messages in the console:
Warning: CFFIXED_USER_HOME is not set! It should be set to the simulated home directory.
Failed to inherit CoreMedia permissions from 59919: (null)
The documentation says something about using NSFileCoordinator and NSFilePresenter. I couldn't find out how to use them though. I'm sure I'll need to tell them the app group identifier somewhere but I don't where.
I got it to work. Since I don't need to write the file from the extension, I don't think I need to use NSFileCoordinator. Turns out NSFileManager has a method containerURLForSecurityApplicationGroupIdentifier() for retrieving the container URL.
let manager = NSFileManager()
let containerURL = manager.containerURLForSecurityApplicationGroupIdentifier("group.MyGroupIdentifier")
let filePath = "\(containerURL.path)/config.plist"
let settings = NSDictionary(contentsOfFile: filePath)
Related
I have an app say called MyApp it is primarily wkwebview. It is already integrated to Files app so I can import/export files from my wkwebview to be able to download files to local machine or upload files to the server.
Finally I am trying to build a UIbutton in my app that will allow my users to Jump into Files app in the folder where I am storing all their content. Instead of building a full FileProviderUI into my app, I just want the button to take the user into the Files App navigating to the folder.
When I give the path for UIDocumentInteractionController to be a directory and do a shared open to present it, nothing happens. No error, nothing at at all. I just want the user to be able to Jump into the folder called MyApp inside the Files app.
I thought it will be very simple. Adding a FileProvider extension or FilePRoviderUI extension seems superflous to just jump the user into this folder and let him interact with Files app to do whatever he likes - open/delete/modify document.
I have to assume that the users are not savvy enough to know even if I tell them that files are saved in Files App for them to be able to interact with directly when they are offline!
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// Just jump to the folder forcing Files app to open this
let viewer = UIDocumentInteractionController(documentsUrl)
viewer.delegate = self
viewer.presentPreview(animated: true)
Nothing gets presented to the user and nothing happens. The button tap just quietly fails.
I think I figured this out. There is a specific URL format that will automatically open the Files app it is shareddocuments:// - Here is a simple function that seems to achieve this quiet simply.
func openSharedFilesApp() {
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let sharedurl = documentsUrl.absoluteString.replacingOccurrences(of: "file://", with: "shareddocuments://")
let furl:URL = URL(string: sharedurl)!
UIApplication.shared.open(furl, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
}
Thanks, Vijay. Here it is in Objective-C.
- (void) openSharedFilesApp
{
NSArray<NSString *>* directories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); // yes = expand tilde
NSURL* url = [NSURL fileURLWithPath:directories.firstObject];
NSString* sharedDocumentPath = [url.absoluteString stringByReplacingOccurrencesOfString:#"file://" withString:#"shareddocuments://"];
NSURL* sharedDocumentURL = [NSURL URLWithString:sharedDocumentPath];
[UIApplication.sharedApplication openURL:sharedDocumentURL options:#{UIApplicationOpenURLOptionUniversalLinksOnly: #(NO)} completionHandler:^(BOOL success) {
}];
}
Adding to #Vijay's answer in iOS 15, Swift 5:
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let sharedurl = documentsUrl.absoluteString.replacingOccurrences(of: "file://", with: "shareddocuments://")
let furl:URL = URL(string: sharedurl)!
if UIApplication.shared.canOpenURL(furl) {
UIApplication.shared.open(furl, options: [:])
}
Below are the two functions which I use in the picker view controller to save the file in the App documents directory. And save the url in the local DB & display the image using UIImage(contentsOfFile: ImagePathString)
Everything works well as until this point however as soon as i re-run the simulator images does not show up. I believe its because the app directory keeps changing every time its run on the simulator. My question if its the same when deployed on the device?
func getDocumentsDirectory() -> NSString {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentsDirectory = paths[0]
return documentsDirectory
}
//Below function is used in imagePickerController
if let data = UIImagePNGRepresentation(modifiedImage!) {
let filename = getDocumentsDirectory().stringByAppendingPathComponent("\(NSDate()).png")
data.writeToFile(filename, atomically: true)
pathImage = filename
print(pathImage)
}
Yes url to App documents folder may change when the app is deployed on the device.
Refer below link
Technical Note TN2406
.
I'm transferring a file from iOS to WatchOS. applicationContext and userInfo transfers all work fine. When I run the following code, nothing is transferred. fileTransfer.transferred == false.
let fileTransfer = WCSession.defaultSession().transferFile(fileurl, metadata: dict)
I can see all of my attempts queued up when I look at:
let transfers = WCSession.defaultSession().outstandingFileTransfers
Is there something I can check that will provide feedback as to why nothing is getting transferred? I'm using simulators. The file is a plist file. I'm accessing it like this:
let fileurl = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("myfile", ofType: "plist")!)
fileurl does return the file path.
I need to be able to reference a file which is stored in my Xcode project in the following way:
I want to be able to use files which I have stored inside of the "data" folder.
How do I reference it to be able to read its contents? What is it's directory path?
I just figured it out. The apple swift documentation and other developer references are really unclear about it.
The way you would reference the "data" folder so as to scan the entire contents is by writing the following:
let path = NSBundle.mainBundle().resourcePath?.stringByAppendingPathComponent("data")
var error: NSError?
let filesInDirectory: [String]! = fileManager.contentsOfDirectoryAtPath(path!, error: &error) as? [String]
This will return the contents of the files in the "data" folder as an array of filenames.
Hope this helps :)
Relative to swift 3
let bundleURL = Bundle.main.bundleURL
let dataFolderURL = bundleURL.appendingPathComponent("data")
let fileURL = dataFolderURL.appendingPathComponent("file.txt")
print(fileURL.path)
print(FileManager.default.fileExists(atPath: fileURL.path))
let bundleURL = NSBundle.mainBundle().bundleURL
let dataFolderURL = bundleURL.URLByAppendingPathComponent("data")
let fileURL = dataFolderURL.URLByAppendingPathComponent("fileName.txt")
I'm working on an custom emoji keyboard in Swift and I'm having trouble finding and counting the images in a folder reference called "emojis".
EDIT: To clarify my issue is that let contents always end up as nil.
The structure from the location of the .xcodeproj file looks like this:
EmojiBoard/emojis/emoji-0.png and so on.
I've been trying to use the NSFileManager with no luck.
let fileManager = NSFileManager.defaultManager()
let contents = fileManager.contentsOfDirectoryAtPath("emojis", error: &error)
println(contents)
This prints nil. I've also tried "EmojiBoard/emojis" and "/EmojiBoard/emojis".
I need this to determine how many images there are and loop them all out without having to resort to an insane switch statement or something like that.
Thank you!
P.S. Please note that I'm coding in Swift, not Objective C. I'm not proficient enough to convert C programming to swift I'm afraid. D.S.
if you created folder reference when adding the folder to your project use it like this (emojis folder icon is a blue folder):
let resourceURL = Bundle.main.resourceURL!.appendingPathComponent("emojis")
var resourcesContent: [URL] {
(try? FileManager.default.contentsOfDirectory(at: resourceURL, includingPropertiesForKeys: nil)) ?? []
}
let emojiCount = resourcesContent.count
print(emojiCount)
if you created groups when adding the folder to your project use it like this (emojis folder icon is a yellow folder):
let resourceURL = Bundle.main.resourceURL!
let resourcesContent = (try? FileManager.default.contentsOfDirectory(at: resourceURL, includingPropertiesForKeys: nil)) ?? []
let emojiCount = resourcesContent.filter { $0.lastPathComponent.hasPrefix("emoji-") }.count
print(emojiCount)
From the top of my head, without access to an IDE to test this code, I reckon something like this:
let fileManager = NSFileManager.defaultManager()
let contents = fileManager.contentsOfDirectoryAtPath(path, error: &error)
for var index = 0; index < contents.count; ++index {
println("File is \(contents[index])")
}
If you replace 'path' above with your documents directory, this code should loop through the whole folder and print out all files.
If you just want the count of items just do this:
println("count is \(contents.count)")
The problem (or at least a major part of the problem) is your path. You can't pass in a path that's just a filename. You need an absolute path to one of the sandboxed directories available to your app like the documents directory.
Your code might look like this:
let documentsDir = NSSearchPathForDirectoriesInDomains(
NSSearchPathDirectory.DocumentDirectory,
NSSearchPathDomainMask.UserDomainMask,
true)[0] as! NSString
let emojisPath = documentsDir.stringByAppendingPathCompnent("emojis")
let contents = fileManager.contentsOfDirectoryAtPath(emojisPath,
error: &error)
println(contents)
(That would work if your emojis folder is in your app's documents folder. If instead your emojis are in your app bundle (built into the app) you would need to use different code entirely (using NSBundle functions to get a path to the directory inside the bundle).
EDIT:
If you want to find files in your app's bundle use the NSBundle method resourcePath, and then append the folder name to the bundle's resourcePath using stringByAppendingPathCompnent, like the code above.