File Not Created in iPhone Documents - ios

I'm trying to save a bunch of GPS locations in a text file on an iPhone. I've been following suggestions that I've gleaned from the web, but, while the app runs fine, no file is being created. At least, nothing is visible under XCode Window -> Devices and Simulators -> (app name).
The goal is to create a file named "1" in a folder called "position" under a folder named after the date/time the app is launched, which is in turn under the Document folder. Example: 2018-07-21-16-35-43/position/1
I'm writing this in Swift 4.2 in XCode 9.(latest)
I get the Document folder as follows (rideStart = Date()):
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
if let docUrl = urls.first {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd-HH-mm-ss"
let dateText = dateFormatter.string(from: rideStart)
positionUrl = docUrl.appendingPathComponent(dateText)
}
When I'm ready to write out some lines of text I do the following (locationCache is an array of CLLocation objects, and textForFile is an extension property that formats the information I want as a comma-separated set of values, terminated with \r\n):
try? FileManager.default.createDirectory(at: positionUrl, withIntermediateDirectories: true)
let curUrl = positionUrl.appendingPathComponent("\(fileNumber)")
for loc in locationCache {
do {
let line = loc.textForFile
print(line)
try line.write(to: curUrl, atomically: true, encoding: .utf8)
}
catch {
print(error)
}
}
No errors are thrown when the code runs, and the text lines print to the console in the expected format.
I'm new to iOS and to Swift, the vast majority of my experience being in C# under Windows, along with javascript. I'm obviously missing something simple and/or basic. What am I doing wrong?
Additional Info
Based on feedback, I ran the app in a simulator. After figuring out where the iPhone 6 simulator was located in my Library folder, I determined that the file I was trying to create was, in fact, created (note: I'm currently using a FileHandle based approach to writing the file, as opposed to what's in the code shown in this posting -- but I've tested that FileHandle approach against the physical iPhone hardware, and it still doesn't create any file visible from within XCode's Devices and Simulators window.
Which brings up a point raised in the comments: is it even possible to examine the files on a connected/paired iPhone from within XCode? I thought from what I've read in various places that it should be possible...but that may not be the case.

Related

Get Reference to SceneKit Catalog Swift

I must be missing something simple - I'm trying to iterate through the
files in an .scnassets folder but can't seem to get a reference to the
folder/catalog.
Here's the organizer:
Models.scnassets contains one file. I tried this:
let fileManager = FileManager.default
let urls = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
let docsURL = urls.first
let assetFolderPath = docsURL?.appendingPathComponent("Models.scnassets").path
And I tried this and several other variants:
let modelPathString = "ARAds/Models.scnassets"
Then I attempted to count the files with both of the above paths:
do {
let modelPathDirectoryFiles = try fileManager.contentsOfDirectory(atPath: assetFolderPath!)
//or modelPathString
print(modelPathDirectoryFiles.count)
} catch {
print("error getting list of files")
}
I get the error message in all cases.
I assume there must be some special way to get a reference to a SceneKit
Catalog but I have not been able to find that in the Apple docs nor SO.
Any guidance would be appreciated. iOS 11.4 Xcode 10.0
Looking at your code, the first thing I see, is that you are referencing the Documents Directory which isn't actually where your folder is.
The Documents Directory is a folder on your actual device where you might save user data or other files which can be accessed at a latter date (for example via iTunes Sharing).
If you move your Models.scnassets folder under the yellow ArAds folder you should be able to access your content like so:
let myModelToLocate = SCNScene(named: "Models.scnassets/Phone_01.scn")
Hope it helps...
You've pointed me in a better direction, but I can already get the scn and make a node if I know the name. What I really want to do is populate a tableview with the options that are available - so I need to find the current list of scene files.
I think this will work:
let subdir = Bundle.main.resourceURL!.appendingPathComponent("Models.scnassets").path
do {
let modelPathDirectoryFiles = try fileManager.contentsOfDirectory(atPath: subdir)
print(modelPathDirectoryFiles.count) //this works
//then do my thing with the array
} catch {
print("error getting list of files")
}
This seems to work, whether I move the folder or not.

Using an existing database in an iMessage app (Swift)

Currently building an iMessage app, and would like to experiment with using a database. I have a database that I would like to use in the app, and have included it in my project, and verified the target membership is correct. Using SQLite.Swift.
Whenever I try opening the connection to the database in simulator, I always get an error (unexpected nil) for the path of the database.
I've tried an image file the same way with no avail.
let imagePath = Bundle.main.path(forResource: "db", ofType: ".sqlite")
do {
let db = try Connection(imagePath!, readonly: true)
} catch {
}
I believe the issue is more related to what an iMessage "app" is - which is actually an extension, not a true app. There's no initial VC, thus no real Bundle.main to get to.
One (maybe soon a second) app of mine has a Photo Editing Extension - basically what I always have called a "shell connection" to an Apple app. You really have either a "do nothing" app with a connection to one of their apps, or you have a stand-alone app an share the code with the extension.
My solution for sharing code is to use a Framework target. Yes, a third project. (App, extension, shared code.) I found a technique that I think should work for you - basically, for images, scripts (my apps use .cikernel files) you add them into the framework project and return what you need in a function call.
You may be able to streamline this with a need for a Framework target. YMMV. The basics are this:
Someplace in Xcode you have a "Bundle Identifier". Something like *"com.company.projectname".
Put your files into a folder, maybe on your desktop. Add an extension to this folder called ".bundle". macOS will give you a warning, accept it. All you are really doing is creating your bundle.
Drag this into your Xcode project.
Code to get to this bundle, and the files inside it. (I'm not sure if need a framework here - try to drag this into your "MessagesExtension" target first.
So lets say you have images you wish to share between projects, extensions, whatever. After moving them into a folder called "images", andrenaming the folder with a ".bundle" at the end, and finally dragging it into your Xcode project, you pretty much need to add this function:
public func returnImage(_ named:String) -> UIImage {
let myBundle = Bundle.init(identifier: "com.company.project")
let imagePath = (myBundle?.path(forResource: "images", ofType: "bundle"))! + "/" + named
let theImage = UIImage(contentsOfFile: imagePath)
return theImage!
}
For a text file you want:
public func returnKernel(_ named:String) -> String {
let myBundle = Bundle.init(identifier: "com.company.project")
let kernelPath = (myBundle?.path(forResource: "cikernels", ofType: "bundle"))! + "/" + named + ".cikernel"
do {
return try String(contentsOfFile: kernelPath)
}
catch let error as NSError {
return error.description
}
}
Usage, for an image called "Camera.png" which is part of a bundle called "images.bundle":
let cameraImage = returnImage("Camera")
Since I don't work with SQLite files I don't have the exact code, but I think this should work. Remember to change "com.company.project" to what you have for the bundle identifier.

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.

Does using a path from system, while calling a file, cause crashes?

I've a text file that stores my app's data for me.
If I use system path (/Users/username/Desktop/ProjectFile/data.txt), can it cause errors when application compiled into .ipa file?
open the file in your project, and check the target membership, as shown in following image. If target membership icon is checked for your corresponding target, then it will not make any problem during compilation(creating ipa). But if target membership is unchecked for your target build, then it will make the problem.
Using path form system like in the question causes crash when tested on a real iOS device.
Instead of using system path, using the code below will be more advantageous. It prevents crashes on real device.
let path = NSBundle.mainBundle().pathForResource("file", ofType: "txt")
let fileMgr = NSFileManager.defaultManager()
var array: [String] = []
if fileMgr.fileExistsAtPath(path!) {
do {
let text = try! String(contentsOfFile: path!, encoding: NSASCIIStringEncoding)
array = text.componentsSeparatedByString("|#|")
}
}

Application ID of NSSearchPathForDirectoriesInDomains Changes per Simulator Run & Update App on Real Device

I'd like to persist images to user domain of iPhone so I write the following code.
let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
print(path)
It shows like: /Users/xxx/Library/Developer/CoreSimulator/Devices/1312F880-6BDC-45D2-B3B3-4D2374313C67/data/Containers/Data/Application/A2850237-5E71-4373-81A6-B443032E1951/Documents/
In this case, Application ID is A2850237-5E71-4373-81A6-B443032E1951
And the problem is when I run on simulator again WITHOUT REMOVING THE APP, it shows like: /Users/xxx/Library/Developer/CoreSimulator/Devices/1312F880-6BDC-45D2-B3B3-4D2374313C67/data/Containers/Data/Application/1F9B5B0A-5A6C-4098-BF40-C978C60C93AF/Documents/
In this case, Application ID is 1F9B5B0A-5A6C-4098-BF40-C978C60C93AF
So there are Application ID difference between previous and current install although I just did update the app and didn't remove the app. Why it is caused and how to fix it?
It causes Xcode 7.2, 7.1, 7.0. And it causes with not only simulator install but also actual device install. So if iOS users update the app from app store, the Application ID will be changed and app sandbox will also be changed and finally users cannot refer their images.
Similar Situations:
Xcode 6.3 seems to change the path to the Documents directory per app run
xcode 6.2 create a new simulator path every time when run the app
Related Guidelines:
File System Basics
Thanks in advance.
EDIT
It seems I have to persist path as relative not absolute.
Application folder name changes every time i run in simulator [duplicate]
Xcode 6 keeps renaming my app's directory in iOS8 simulator after each run.
I'll try the approach and if I solved my problem, I'll update the question.
I have to persist path relative not absolute. And I also can salvage old run images by fetching with NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] + "persisted relative path".
A better solution is to save a bookmark data
https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/AccessingFilesandDirectories/AccessingFilesandDirectories.html
A bookmark is an opaque data structure, enclosed in an NSData object,
that describes the location of a file. Whereas path- and file
reference URLs are potentially fragile between launches of your app, a
bookmark can usually be used to re-create a URL to a file even in
cases where the file was moved or renamed
First you must use NSURL instead of String
Convert an NSURL to NSData
let data: NSData? = try? url.bookmarkDataWithOptions(.SuitableForBookmarkFile, includingResourceValuesForKeys: nil, relativeToURL: nil)
Read an NSURL from bookmark NSData
var isStale: ObjCBool = false
let url = try? NSURL(
byResolvingBookmarkData: bookData,
options: [],
relativeToURL: nil,
bookmarkDataIsStale: &isStale)
guard let fullURL = url else {
return nil
}
You can fill relativeToURL to your document directory url
Alternatively you can use FileKit : path.bookmarkData and Path(bookmarkData: ..)
try to persist your file just with the name.extension
to save document
let pathDocument = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
let fullPath = let fullPath = pathDocument+"/"+"fileName.ext"
//...add some code to save document at `fullPath`...
to get document
let pathDocument = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
let fullPath = let fullPath = pathDocument+"/"+"fileName.ext"
//... add code to get data at path : `fullPath`....

Resources