Permanent file directory in Xcode - ios

In the documents directory for my app, I have a file Q1.dat which I use in my program. I access it through the path:
func createArrayPath () -> String? {
if let docsPath: String = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).last {
return ((docsPath as NSString).stringByAppendingPathComponent("Q1") as NSString).stringByAppendingPathExtension("dat")
}
return nil
}
When I run the program on a different device however, a different documents directory is created, and the file is no longer present. Is there a place I can put the file so that I can always access it no matter what device I am running on?

The Documents directory is meant for data created by the user.
It sounds like you want to bundle a resource with your application. You can do this by simply dragging the file into Xcode, which will automatically set it up to be copied into your app bundle, and you can find it using URLForResource(_:withExtension:).
If the file is very large, you might want it to be downloaded separately from the main app. For this you can use the new On-Demand Resources feature.

Related

Importing Documents from iMessage into App - Deleting temp files placed in "Inbox"

I have defined a UTI for a custom document format. I can export files from my app and append them to text messages, email, etc. I can import the files into my app by tapping on the document icon in iMessage. By tapping on the document icon, I have the option to copy to my app. That triggers a call in my AppDelegate to handle the incoming file.
What's bugging me is that the url for the incoming file is:
file:///private/var/mobile/Containers/Data/Application/21377C94-1C3C-4766-A62A-0116B369140C/Documents/Inbox/...
Whereas, when saving documents to the .documents directory, I use this URL:
file:///var/mobile/Containers/Data/Application/21377C94-1C3C-4766-A62A-0116B369140C/Documents/...
The difference being the /private/ and /Inbox/ path components.
Question: how can I purge the /private/.../Inbox/ path of the files that were copied to my app from iMessage? I noticed this when testing my app and when I tapped on the same document icon in iMessage it started generating file copies with the same name but adding -1, then -2, then -3 to the file name of the document from iMessage. It appears that copies are building up in that /private/.../Inbox/ path.
Is that something that gets flushed on its own or can I access that directory to remove those files? It's also annoying because based upon the filename, it appears to be a different file thus allowing multiple copies of the same file to be imported with a slightly different file name.
Ok, this took a fair amount of digging, but I'll post my solution that seems to work thus far in case anyone runs across the same problem.
let fileManager = FileManager.default
// get the URL for the "Inbox"
let tmpDirURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("Inbox")
// get all the files in the "Inbox" directory
let anythingThere = try? fileManager.contentsOfDirectory(at: tmpDirURL, includingPropertiesForKeys: nil)
if anythingThere?.count ?? 0 > 0 {
for eachURL in anythingThere! {
// for each url pointing to a file in that directory, get its path extension
let pathExtension = eachURL.pathExtension
// test to see if it's a UTI that you're interested in deleting
// in my case, the three "OCC" strings are the relevant UTI extensions
if pathExtension == "OCCrcf" || pathExtension == "OCCrdi" || pathExtension == "OCCsrf" {
// attempt to delete the temporary file that was copied to the
// "Inbox" directory from importing via email, iMessage, etc.
try fileManager.removeItem(at: eachURL)
}
}
}
If anyone has a more elegant solution, please respond as well. Thanks.

iOS reading from a file

I'm trying to get my iPhone app to load text from a file into a string array, with 1 line from the file per array element.
I've created an input file as a text file using sublime text. I dragged the file (which is located inside of a folder inside of my project directory) into xCode into a folder in the same location in the project heirarchy.
I also tried adding it as a bundle (by copying the folder and renaming it with the .bundle extension), to no avail. Currently, my app has the file in 2 places (Obviously I plan to delete the unneeded version, but I'm not sure how this will work so I've left it for now).
I've written a function that I want to read my file, and assemble its contents into an array:
func readFromFile(filename: String) -> [String]? {
guard let theFile = Bundle.main.path( forResource: fileName, ofType: "txt") else {
return nil // ALWAYS returns nil here: Seems 'filename' can't be found?????
}
do { // Extract the file contents, and return them as a split string array
let fileContents = try String(contentsOfFile: theFile)
return fileContents.components(separatedBy: "\n")
} catch _ as NSError {
return nil
}
}
As it stands, the function always returns nil at the location commented in the code.
I've been working on this for ~6hrs (and tried every suggestion I could find on StackOverflow, google etc) and I'm just getting more and more confused by the differences between the various versions of Swift and intricacies of iOS development. I can't seem to find a consistent answer anywhere. I've checked the apple documentation but it's too high level with no example code for me to understand at my swift beginner level.
I also tried naming the file with a ".txt" extension but that didn't help either.
The file must certainly be named alert01.txt if you are going to refer to it as forResource: "alert01", ofType: "txt".
Loading from a bundle will not work. The file needs to be part of your project as shown in the first entry.
However, your code is not going to work because you have created a folder reference. That means the folder PanicAlertFiles is being copied with all its contents into your bundle. Your code will need to dive into that folder in order to retrieve your file. Use path(forResource:ofType:inDirectory:) to do that, or (if you don't want to have to code the file name explicitly) get the folder and then use the FileManager to examine its contents.

How to find Apple App Group shared directory

We are currently developing an iOS10 app, including "Messages Extension".
To share CoreDatas persistant store.sqlite inbetween App and Extension, we are using a shared "Apple App Group" directory, which is working fine.
Now we have to get our hands on the store for debug reasons and are unable to find the directory. The Apps container directories are completely empty, which makes sense. But how to download our database? Do we have to somehow copy it programmatically to a reachable place?
To sum it up:
We already use CoreData which stores model.sqlite in our shared directory.
Everything is up and running.
What we want to archive is to download the database to our computer.
Without a shared directory we can simply download the App container from the device, using Xcode->Devices. But as we do use a shared directory, the .sqlite database is not within the container.
Question:
How can we download the .sqlite database from the device to our computer?
EDIT on 2018-10-12: Updated code for Swift 4.x (Xcode 10). (Older version retained for reference.)
In Swift 4.x:
let sharedContainerURL :URL? = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.etc.etc")
// replace "group.etc.etc" above with your App Group's identifier
NSLog("sharedContainerURL = \(String(describing: sharedContainerURL))")
if let sourceURL :URL = sharedContainerURL?.appendingPathComponent("store.sqlite") {
if let destinationURL :URL = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("copyOfStore.sqlite") {
try! FileManager().copyItem(at: sourceURL, to: destinationURL)
}
}
In older version of Swift (probably Swift 2.x):
let sharedContainerURL :NSURL? = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.etc.etc") // replace "group.etc.etc" with your App Group's identifier
NSLog("sharedContainerURL = \(sharedContainerURL)")
if let sourceURL :NSURL = sharedContainerURL?.URLByAppendingPathComponent("store.sqlite")
{
if let destinationURL :NSURL = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0].URLByAppendingPathComponent("copyOfStore.sqlite")
{
try! NSFileManager().copyItemAtURL(sourceURL, toURL: destinationURL)
}
}
Something like the above will get a file from the app group's shared container to the app's Documents directory. From there, you could use Xcode > Window > Devices to get it to your computer.
You could also use iTunes file sharing to retrieve the file from the app's Documents directory after setting UIFileSharingEnabled to YES in the Info.plist file, but bear in mind that this will expose the directory's contents to the user as well. Should be okay for development/debugging purposes, though.

How to input a default .txt file into an iPhone through Xcode in Swift

I am trying to read from a text file that I have already created on my desktop. I put this Story.txt into the corresponding file path on my computer through Xcode, something along these lines :
(/Users/username/Library/Developer/CoreSimulator/Devices/12F046A7-a lot more numbers/data/Containers/Data/Application/8AECCA06-160C-4702-B16E-FF50B2A145F5)
and the code works perfectly. However, when I try to run the app on my iPhone, it changes the file path and Xcode doesn't automatically transfer my files to my iPhone documents folder at this file path.
(/var/mobile/Containers/Data/Application/139C80A3-more numbers/Documents)
How do I manually put a .txt file at this location?
Here is the code I use to read the file.
func loadStory()
{
if let dir : NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first
{
let path = dir.stringByAppendingPathComponent("Story.txt")
print(path)
do
{
//reads file
let text:String = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding) as String
}
catch {/* error handling here */}
}
}
Since it seems you want the file included with your app, do not attempt to manually install the file somewhere on your computer. That only works with the Simulator if you happen to put the file in just the right place.
Instead, add the file to your Xcode project. Then the file will be added to your app's resource bundle. Then you can access the file using NSBundle to get its path. It will not be in the app's Documents folder. It will be in the app's bundle.
See Where to place a .txt file and read from it in a IOS project for more details.
You need to add it to your project. Drag and drop it inside your project so it'll be bundled in. Than you can access it using
Let string = NSBundle.mainBundle().resourcePath as NSString
String.stringByAppendingPathComponent('Story.txt')
If you could change the file from .txt to plist, you could integrate it on your project, and never think where it is, it will be always accessible.

retrieving and writing a file in iOS

I looked at many SO question where the user wanted to access a document in his app directory. I am new to all of this and just want some clarification on the matter. I am building an open-source content blocker for iOS 9.
You can see the file tree. I wonder if it is possible to access the file called blockerList.json which is in the folder Adblock Content Blocker. My goal was to write the json file based on what the user want to build. I need to be able to modify the content of this file. Is this possible or should I stop trying and leave it like that?
Thanks for any help.
The app bundle is readonly as #dan said. So you have to copy the file from bundle into the Document directory then you able to modify the file at anytime.
Copy file into document like this:
class func copyFile(fileName: String) {
let destPath: String = getPathInDocument(fileName)
var fromPath = NSBundle.mainBundle().pathForResource("blockerList", ofType: "json")!
var fileManager = NSFileManager.defaultManager()
if !fileManager.fileExistsAtPath(destPath) {
fileManager.copyItemAtPath(fromPath, toPath: destPath, error: nil)
} else {
println("The file allready exists at path:\(destPath)")
}
}
class func getPathInDocument(fileName: String) -> String {
return NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0].stringByAppendingPathComponent(fileName)
}
Hope this will help you!
Note : You have readonly permission for application's bundle file.
Firstly copy file in NSDocumentDirectory with backup attribute. Use refrerence copy file to document directory iphone and for How do I prevent files from being backed up to iCloud and iTunes?
Check references ImportingJSONFile and Y6JSONFileManager-iOS to read and write changes in JSON File.

Resources