Reading from ApplicationSupportDirectory in iOS - ios

Using LXLogger I am able to write log entries to file on the device and simulator (in addition to xcode console etc).
The default location which LXLogger stores the file is under the ApplicationSupportDirectory.
LXLogger writes to the file OK, and I can see the file is on my device (or on my mac HDD when using the simulator), but when I try to open the file I am told that the file does not exist.
I have added the error logs generated in line with the code.
I've tried a dozen different methods and multiple questions on here, to no avail.
let directory = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask).first!.URLByAppendingPathComponent(NSBundle.mainBundle().bundleIdentifier!, isDirectory: true).URLByAppendingPathComponent("logs", isDirectory: true)
do {
let array = try NSFileManager.defaultManager().contentsOfDirectoryAtURL(directory, includingPropertiesForKeys: nil, options:[])
var str = array[0].description
str = str.stringByReplacingOccurrencesOfString("file://", withString: "", options: .LiteralSearch, range: nil)
let fileURL = NSURL(fileURLWithPath:str)
log.info("fileURL contains: " + fileURL.absoluteString)
do {
log.info("Attempting to get contentsOfURL")
let data = try NSString(contentsOfFile: array[0].description,
encoding: NSASCIIStringEncoding)
logTextView.text = data as String
} catch let error as NSError {
log.error(error.description)
}
}
catch let error as NSError {
log.error(error.description)
}
Log Outputs from above code:
> 2016-03-31 21:43:52.121 [INFO] fileURL contains: file:///private/var/mobile/Containers/Data/Application/2F812C06-E204-4543-905C-F884C35DA198/Library/Application%2520Support/myname.myapp/logs/1_log.txt
> 2016-03-31 21:43:52.122 [INFO] Attempting to get contentsOfURL
> 2016-03-31 21:43:52.123 [ERROR] Error Domain=NSCocoaErrorDomain Code=260 "The file “1_log.txt” couldn’t be opened because there is no such file." UserInfo={NSFilePath=file:///private/var/mobile/Containers/Data/Application/2F812C06-E204-4543-905C-F884C35DA198/Library/Application%20Support/myname.myapp/logs/1_log.txt, NSUnderlyingError=0x1376e0e00 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
So the code shows that the file is there (and i can physically open the copy on my mac), but the app can't find it.
Presumably its something to do with my usage of the file handle/nsurl/something like that??

Your approach of taking the NSURL from array and using stringByReplacingOccurrencesOfString and fileURLWithPath and then description is wrong; it doesn't correctly handle the percent-encoding in the URL and it's generally complex/fragile.
This can be much simpler, because you already have a file URL returned in the array from contentsOfDirectoryAtURL. You can just use it directly:
let data = try String(contentsOfURL: array[0], encoding: NSUTF8StringEncoding)

Related

Swift File Download Issue

I am trying to download a plist file from a remote location and use it in the iOS app I am creating. The file is going to be used for calendar details within the app's calendar. The goal is obviously that I can update the remote file instead of having to push updates to the app itself every time we need to make changes to calendar details.
I started with the code used in this example: Download File From A Remote URL
Here is my modified version:
// Create destination URL
let documentsUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("2017.plist")
//let destinationFileUrl = URL(string: Bundle.main.path(forResource: String(currentYear), ofType: "plist")!)
//Create URL to the source file you want to download
let fileURL = URL(string: "https://drive.google.com/open?id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.removeItem(at: destinationFileUrl)
try FileManager.default.moveItem(at: tempLocalUrl, to: destinationFileUrl)
print("File was replaced")
print(NSArray(contentsOf: tempLocalUrl))
//print(tempLocalUrl)
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
}
task.resume()
I originally tried to overwrite the file that is bundled with the app to being with, that resulted in errors. So I instead tried to just save it in the app's documents folder and that removed that error. I had to make sure and remove any previous version of the file because it was giving me a file already exists error after the first run.
While it says everything is working (The outputs for both successful download and replaced file happen) when I print the contents of the array from the downloaded URL it just gives me nil.
This is my first attempt to use any kind of external resources in an app. Before I have always kept everything internal, so I am sure there is something glaringly obvious I am missing.
Update 1:
I realized I didn't have the correct URL to use to download a file from a Google drive. That line of code has been changed to:
let fileURL = URL(string: "https://drive.google.com/uc?export=download&id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
So now I actually am downloading the plist like I originally thought I was. Even removing the deletion issue mentioned in the first comment, I still can't get the downloaded file to actually replace the existing one.
Update 2:
I have reduced the actual file manipulation down to the following:
do {
try FileManager.default.replaceItemAt(destinationFileUrl, withItemAt: tempLocalUrl)
print("File was replaced")
print(NSArray(contentsOf: destinationFileUrl))
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
After the replacement is performed the output of the file shows the correct new contents that were downloaded from the internet.
Later in the code when I try and access the file it seems to be nil in content again.
Look at your download completion code. You:
Delete the file at the destination URL (in case there was one
leftover)
MOVE the temp file to the destination URL (removing it from the temp
URL)
Try to load the file from the temp URL.
What's wrong with this picture?
You are trying to get the contents of the moved file. You already moved the file to destination url and then you are trying to get the contents of the file from temporary location.
For getting file data, Please try the following :
let fileData = try! String(contentsOf: destinationFileUrl, encoding: String.Encoding.utf8)
print(fileData)

i have an issue on the file System on the actual device

my problem is when i try to copy images from the photo library to the filesystem i get the error the error is
(Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_0926.JPG”
couldn’t be opened because you don’t have permission to view it."
UserInfo={NSFilePath=/var/mobile/Media/DCIM/100APPLE/IMG_0926.JPG,
NSUnderlyingError=0x15649630 {Error Domain=NSPOSIXErrorDomain Code=1
"Operation not permitted"}})
my code is :
let fileManager = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let document = fileManager[0]
var i = 0
for asset in arrayOfAssets {
let filePath = document.appendingPathComponent("image\(arrayOfAssets.count + i).png")
i += 1
PHImageManager.default().requestImageData(for: asset, options: nil) { (data, nil, _ , info) in
let url = info?["PHImageFileURLKey"] as! NSURL
do {
try FileManager.default.copyItem(at: url as URL, to: filePath)
}catch {
print(error)
}
// print(filePath)
}
}
please if anyone can help me
thanks all
It sounds like you're trying to open a file located here : NSFilePath=/var/mobile/Media/DCIM/100APPLE/IMG_0926.JPG
By default access to this directory is not permitted, you can only access files located in the document directory, for security reasons.
I don't find where this URL comes from in your code, but according to the NSError you are trying to access an image outside the permitted area of the file system.
Please check the permission option in plist for Photos Library in iOS 11.
Also you can save data in TEMP directory and fetch it when needed and after use
clean data in TEMP directory.

swift 2 - file not found error when reading text file from app main bundle

I added a simple text file (savedinfo.txt) to my project containing just 1 line of text. I need to read the text from within my app. I did that as below. However, for some reasons, it cannot find the file when reading it, even though sFilePath shows the file path. See debug msg below. I am using the latest Xcode 7.2.
Thanks
let sFilePath = String(NSBundle.mainBundle().pathForResource("savedinfo", ofType: "txt")
let sText = try NSString(contentsOfFile: sFilePath, encoding: NSUTF8StringEncoding)
sFilePath: Optional("/Users/user115387/Library/Developer/CoreSimulator/Devices/F3866C2A-869A-4CE5-9936-C8BC0CC5CE0D/data/Containers/Bundle/Application/A98B6D45-1C9A-4049-86F1-9502D45836B0/TableViewDemo.app/savedinfo.txt")
error in reading file Error Domain=NSCocoaErrorDomain Code=260 "The file “savedinfo.txt")” couldn’t be opened because there is no such file."
This code works for me:
if let sFilePath = NSBundle.mainBundle().pathForResource("savedinfo", ofType: "txt") {
do {
let sText = try NSString(contentsOfFile: sFilePath, encoding: NSUTF8StringEncoding)
print(sText)
}catch {
}
}

Swift: copying files gives Cocoa Error 262

I'm trying to move my files up a level in some random directory in the default Documents directory. But when copying, Swift gave me the error Cocoa Error 262: NSFileReadUnsupportedSchemeError. Why did I get this error? I ran a quick search and pretty much everyone who'd encountered this problem was trying to copy files out of the camera roll, but I'm not. Any thoughts? Thanks in advance.
Code here:
private func copyFilesInDirectory(fromDir: String, toDirectory toDir: String, withCompletionHandler handler: ()->()){
println("from: \(fromDir)\nto: \(toDir)")
var error: NSError?
var contents = fileManager.contentsOfDirectoryAtPath(fromDir, error: &error)
if let dirContents = contents{
let enumerator = (dirContents as NSArray).objectEnumerator()
while let file = enumerator.nextObject() as? String{
let filePath = fromDir.stringByAppendingString("/\(file)")
println("copying \(filePath)")
if(fileManager.copyItemAtURL(NSURL(fileURLWithPath: filePath)!, toURL: NSURL(string: toDir)!, error: &error)){
println("COPIED")
}
else{
println("COPY ERROR: \(error!.localizedDescription)")
}
}
handler()
}
}
and here's the log if anyone's interested:
from: /Users/dolce/Library/Developer/CoreSimulator/Devices/05566649-BABB-44BE-BE6F-AFF7B41E3065/data/Containers/Data/Application/F5A83C94-C177-4826-BDD5-3A50E7508239/Documents/Hello/tmp
to: /Users/dolce/Library/Developer/CoreSimulator/Devices/05566649-BABB-44BE-BE6F-AFF7B41E3065/data/Containers/Data/Application/F5A83C94-C177-4826-BDD5-3A50E7508239/Documents/Hello
copying /Users/dolce/Library/Developer/CoreSimulator/Devices/05566649-BABB-44BE-BE6F-AFF7B41E3065/data/Containers/Data/Application/F5A83C94-C177-4826-BDD5-3A50E7508239/Documents/Hello/tmp/test.png
COPY ERROR: The operation couldn’t be completed. (Cocoa error 262.)
The toURL parameter of copyItemAtURL() must not be the destination
directory, but the URL of the destination file. This can be
created with
let destPath = toDir.stringByAppendingPathComponent(file)
You should also replace
let filePath = fromDir.stringByAppendingString("/\(file)")
with
let filePath = fromDir.stringByAppendingPathComponent(file)
There is also a copyItemAtPath() method which saves you from
converting the paths to URLs.
On an iOS device, the simple answer is you can't do that. On the simulator things are little different. What you're probably dealing with is file permissions on the directory you're trying to write to.
As a general rule on iOS, you can write to the designated directories like the documents/caches and nothing else.

Load .m4a file from disk into NSData object - iOS

I recorded the users voice with an AVAudioRecorder and saved it as a .m4a file in the documents folder. When I am trying to load the .m4a file into an NSDataobject, the object is nil.
The .m4a file contains sound and the used file path is correct.
I am using the following line to load the .m4a file:
let soundData = NSFileManager.defaultManager().contentsAtPath(soundPath)
println("\(soundData)") // prints nil
It appears that the AVAudioPlayer requires a path with a "file://" prefix, while SFileManager.defaultManager().contentsAtPath() needs this prefix removed from the path in order to work properly. Try removing this by doing something like:
soundPath = soundPath.stringByReplacingOccurrencesOfString("file://", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
From the docs: "If path specifies a directory, or if some other error occurs, this method returns nil."
The The best way to check for the file is in Xcode: Menu Windows:Devices. Select the device connected to the computer, double click the app, Examine the file contents.
println("sound path: (soundPath)"), bets are it is not correct.
Consider using the NSData method, it has an error parameter to better report a failure:
+ (id)dataWithContentsOfFile:(NSString *)path options:(NSDataReadingOptions)mask error:(NSError **)errorPtr
Example with error reporting:
let error:NSError
let soundData = NSData().contentsOfFile: soundPath options: 0 error: &errorPtr)
if soundData != nil {
// Do something
}
else {
println("error: \(error!.localizedDescription)")
}
Example error message:
error: The file “TestSound.m4a” couldn’t be opened because there is no
such file.
BTW, a more clear name for the path: sound might be soundPath.
Release the AVAudioRecorder before trying to load the url to NSData.

Resources