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.
Related
I have an HTML file (webview.html, for now) with relative links to images and .js files. I need to load it, and those linked resources into an iOS app's WKWebView. All the files are stored in Resources/Non-Localized/.
I'm attempting to load the files using the following code during viewDidLoad():
print("loadWebView: Bundle.main = ", Bundle.main); // This prints
if let htmlUrl = Bundle.main.url(forResource: "webview", withExtension: "html") {
print("htmlUrl = ", htmlUrl) // Doesn't print
webView.load(URLRequest.init(url: htmlUrl))
}
The app loads without error, but also without the contents in the webview.
I get the first print statement, but not the second, indicating something is wrong with my URL or resource bundle configuration.
What am I doing wrong? Do I need to do anything in XCode to add these files to the project? Where should I be looking for error messages that will hint me in the correct direction in the future?
I needed to add the file to the Project. I did this by simply dragging the Finder file icon to the project tree in Xcode.
Alternatively (and my final solution), I dragged the parent directory's icon, making a folder reference, and then added subdirectory parameter to .url(forResource...).
I'm still working on getting informative errors out of the WebView.
I'm trying to delete the files in my cache directory. However, when I tried to delete individual folders, I got an error saying the file doesn't exist, even though I know it does. So, I tried using a for loop to delete the all the files in the caches directory.
do {
for file in try NSFileManager.defaultManager().contentsOfDirectoryAtPath(cacheFolderPath) where !file.hasPrefix("."){
try NSFileManager.defaultManager().removeItemAtPath(file)
}
print("Cache cleared successfully.")
}
catch let error as NSError {
print(error.localizedDescription)
if let reason = error.localizedFailureReason {
print(reason)
}
}
}
However, this code prints this:
"CategoryThumbnails" couldn't be removed.
The file doesn't exist.
Well, it obviously does exist, as it was discovered by the contentsOfDirectoryAtPath method! How can it not exist? Does anybody know what's going on here, and how I can clear the cache?
The results of contentsOfDirectoryAtPath is a list of files in the given folder. But each of those results is not a complete path itself.
To delete each file, you need to append file to cacheFolderPath. Then pass that complete path to removeItemAtPath.
Another solution is to use one of the other folder enumeration methods of NSFileManager that return full URLs and let your provide options to skip hidden files.
I am trying to use a bundled realm file without success. I know that my realm file was copied successfully to my application’s Directory but I ca not read it.
fatal error: 'try!' expression unexpectedly raised an error: "Unable
to open a realm at path
'/Users/…/Library/Developer/CoreSimulator/Devices/…/data/Containers/Data/Application/…/Documents/default-v1.realm'.
Please use a path where your app has read-write permissions."
func fullPathToFileInAppDocumentsDir(fileName: String) -> String {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,NSSearchPathDomainMask.UserDomainMask,true)
let documentsDirectory = paths[0] as NSString
let fullPathToTheFile = documentsDirectory.stringByAppendingPathComponent(fileName)
return fullPathToTheFile
}
In didFinishLaunchingWithOptions:
let fileInDocuments = fullPathToFileInAppDocumentsDir("default-v1.realm")
if !NSFileManager.defaultManager().fileExistsAtPath(fileInDocuments) {
let bundle = NSBundle.mainBundle()
let fileInBundle = bundle.pathForResource("default-v1", ofType: "realm")
let fileManager = NSFileManager.defaultManager()
do {
try fileManager.copyItemAtPath(fileInBundle!, toPath: fileInDocuments)
} catch { print(error) }
}
And setting the configuration used for the default Realm:
var config = Realm.Configuration()
config.path = fileInDocuments
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm(configuration: config) // It fails here!!! :-)
As the documentation suggests, I have tried as well to open it directly from the bundle path by setting readOnly to true on the Realm.Configuration object. I am not sure if this is something related to Realm or if I am overlooking something with the file system…. I have also tried to store the file in the Library folder.
Realm 0.97.0
Xcode Version 7.1.1
I tried to open the realm file using Realm's browser app and the file does not open anymore. It has now new permissions: Write only (Dropbox). So, I decided to change the file permission back to read/write using file manager’s setAttributes method. Here is how I did it:
// rw rw r : Attention for octal-literal in Swift "0o".
let permission = NSNumber(short: 0o664)
do {
try fileManager.setAttributes([NSFilePosixPermissions:permission], ofItemAtPath: fileInDocuments)
} catch { print(error) }
The realm file can now be open at this path.
That exception gets thrown whenever a low level I/O operation is denied permission to the file you've specified (You can check it out on Realm's GitHub account).
Even though everything seems correct in your sample code there, something must be set incorrectly with the file location (Whether it be the file path to your bundle's Realm file, or the destination path) to be causing that error.
A couple of things I can recommend trying out.
Through breakpoints/logging, manually double-check that both fileInDocuments and fileInBundle are being correctly created and are pointing at the locations you were expecting.
When running the app in the Simulator, use a tool like SimPholders to track down the Documents directory of your app on your computer, and visually ensure that the Realm file is being properly copied to where you're expecting.
If the Realm file is in the right place, you can also Realm's Browser app to try opening the Realm file to ensure that the file was copied correctly and still opens properly.
Try testing the code on a proper iOS device to see if the same error is occurring on the native system.
If all else fails, try doing some Realm operations with the default Realm (Which will simply deposit a default.realm file in your Documents directory), just to completely discount there isn't something wrong with your file system
Let me know how you go with that, and if you still can't work out the problem, we can keep looking. :)
This will occur if you have your realm file open in Realm Studio at same time you relaunch your app. Basically in this case Realm can't get write permissions if Studio already has them.
To add to the solution based on what I've discovered, make note of what error Realm reports when it throws the exception, as well as the type of Error that is passed.
As of writing, Realm documents their errors here:
https://realm.io/docs/objc/latest/api/Enums/RLMError.html
What this means is that you can find out if your Realm file has permissions problems and react to those based on Realm passing you a RLMErrorFilePermissionDenied. Or if the file doesn't exist with RLMErrorFileNotFound.
The tricky thing I'm finding is when you get a more generic RLMErrorFileAccess, but that's for another question on Stack Overflow...
I had the same issue and tried too many ways to fix it. The easiest way to fix this problem is manually creation of the folder XCode cannot reach '/Users/…/Library/Developer/CoreSimulator/Devices/…/data/Containers/Data/Application/…/Documents/...' as explained at https://docs.realm.io/sync/using-synced-realms/errors#unable-to-open-realm-at-path-less-than-path-greater-than-make_dir-failed-no-such-file-or-directory
Once you created this folder and run the project, XCode creates the Realm files inside this folder automatically.
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.
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.