Using Swift Playgrounds to read a file from a user path - swift

I have downloaded a file using my iPad into the user Downloads folder, not in the resource bundle for the Playground [footnote].
Is there any way to read it using a Playground? I’ve tried a whole bunch of different things and nothing seems able to access the file.
Here’s an example of what doesn’t work for me:
// Playground to analyze my finances downloaded from my bank
import SwiftUI;
import Foundation;
// Read in transactions
var filename = "filename.txt"
do {
print("Letting dir")
let dir = try! FileManager.default.url(for: .downloadsDirectory, in: .userDomainMask, appropriateFor: nil, create: false);
print("Dir set, appending file")
var fileurl = dir.appendingPathComponent(filename)
print("Letting contents of file \( fileurl )");
let contents = try String(contentsOf: fileurl, encoding: .utf8)
// Process Transactions
print(contents)
// Display results
}
catch {
print("Error opening file: \( error )")
}
I think the problem is that it’s accessing the downloads folder of the resources bundle, instead of the system bundle, but I don’t know how to fix that or if it even can be done.
Thanks for any help. This has been unbelievably frustrating and it’s the very first step of my project.
David
[footnote] I can’t even figure out how to access the resource bundle - all the GUI lets me do is edit the Playground code. And I understand the resource paths frequently change, so that doesn’t seem like the right thing to do anyway.

Related

Persisting with FileManager is producing unique urls at every app launch in Simulator

I want to persist data, images and otherwise. I read that saving to files is especially recommended for larger data. I followed relevant parts of this tutorial: https://developer.apple.com/tutorials/app-dev-training/persisting-data.
However, the URL in the way mentioned, every time I launch, it produces a new url.
public extension FileManager {
func with(hash: String) throws -> URL {
try url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathExtension(hash)
}
}
Then when I call this:
let localURL: URL = try! FileManager.default.with(hash: deterministicHash + "downloadData")
print(localURL.absoluteString)
Produces this on the first launch:
file:///Users/name/Library/Developer/CoreSimulator/Devices/376FF910-242B-4E2E-9E32-F9CE845EB1BE/data/Containers/Data/Application/3B28DBA9-8C2E-4CE5-A92A-F4C4DDB106AC/Documents.downloadData/
And this on the second:
file:///Users/name/Library/Developer/CoreSimulator/Devices/376FF910-242B-4E2E-9E32-F9CE845EB1BE/data/Containers/Data/Application/5F5EC7DC-2A41-402C-8265-EF99F50D2C90/Documents.downloadData/
I just want to persist data that can securely accommodate images. How can I either get a consistent url to use? Or otherwise get around this issue?
UPDATE
I implemented this tutorial:
https://developer.apple.com/tutorials/app-dev-training/persisting-data
Even after following this tutorial, I am not able to persist. Is there a computer setting I need to enable/disable?
The issue there is that your app is sandboxed. Just don’t save the whole url. What you need is to save the directory and file name and reconstruct your URL at every launch. Btw you should use pathComponent not pathExtension when composing your URL.
try url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent(hash)

Trouble with Application Support directory

I searched for my problem with identifying location of the "Application support" on SO. However the various solutions I found are not working for me. Here's the code I'm using to locate a file I want to read/write from.
private var databaseUrl: URL?
{
let file = "MyApp/MyFile.txt"
if let dir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
{
let path = dir.appendingPathComponent(file)
return path
}
return nil
}
The Bundle Identifier of my app I have set to "MyApp" instead of "com.xxx.yyy.Myapp" which Xcode was using by default, and which I don't want. I was expecting to get a path like this...
/Users/griftopia/Library/Application Support/MyApp/MyFile.txt
This would be consistent with how I see other apps create files on my machine. However, what I am actually getting is this...
Users/griftopia/Library/Containers/MyApp-66665055012bb589084e4aeda209f212b5f695c7/Data/Library/Application Support/Myfile.txt)
Needless to say I want to say within the sandbox, but I thought Application Support is in the sandbox, but not expecting the path I got. Appreciate any insight

Accessing File saved in today extension from project

I am trying to access a file I saved from my today extension.In my today extenson I did this to save the file:
func startRecording() {
let audioFilename = getDocumentsDirectory().appendingPathComponent("recording.m4a")
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
audioRecorder.delegate = self
audioRecorder.record()
recordButton.setTitle("Stop", for: .normal)
} catch {
finishRecording(success: false)
}
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
I then tried to get the data for my AVAudio Player in the main part of the project using this code:
let path = Bundle.main.path(forResource: "recording.m4a", ofType:nil)!
let url = URL(fileURLWithPath: path)
However, it gave the error: unexpectedly found nil while unwrapping an Optional value.
Thanks for the help.
Your extension saves the file to its document directory and your app code is looking for the file in the app bundle. The app bundle only contains the resources that are distributed with the app. You'll need to delve into the file system.
However, there's another problem. The extension and containing app don't share a documents directory. They each have their own container for writing data local to themselves. If you want to share data between them, it's a little more work. In summary:
Create an app group identifier for the app and the extension to share.
Use FileManager.containerURL(forSecurityApplicationGroupIdentifier:) to get the file URL for the shared container directory.
From the container URL, append the file name.
In the extension, you'll set up the AVAudioRecorder as usual and start recording.
In the main app, you'll want to use the NSFileCoordinator API to ensure that only one process is writing to the file at a time. Hopefully, the AVAudioRecorder uses the NSFileCoordinator API internally, although I didn't immediately find confirmation of this.
For more details about shared containers, see this blog post.
I just tried the same - record audio from a Today Extension. The code looks sooo familiar, so I'm taking a wild guess: you want to capture voice and send the file to the Google Speech API, correct?
Nonetheless, I think we're hitting restrictions of extensions: judging by https://developer.apple.com/library/content/qa/qa1872/_index.html extensions cannot record audio. The article has been writting for iOS 8, but I don't believe Apple ever lifted the restriction. Please correct me if I'm wrong, since I keep running into problems doing what OP does myself.
btw, check the result of audioRecorder.record(). It might be false and indicate that the capture never started (that's my error currently).

How to get the Log Directory in Swift?

I see the ability to get the path to the ApplicationsSupport Directory using Swift.
Is there an interface to get the Log directory?
As far as I can see, there is no Apple-provided way to get this. This seems odd at first glance, but I believe Apple's rationale is that we are actually not supposed to need to directly access this directory, and instead should use their logging API which takes care of these details.
However, I'm using SwiftyBeaver to write logs to a file, and used the following code to generate the logs directory URL:
let bundleID = Bundle.main.bundleIdentifier!
let logsUrl = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0]
.appendingPathComponent("Logs", isDirectory: true)
.appendingPathComponent(bundleID, isDirectory: true)

Swift - Listing contents of a non-local directory

I'd like to list the contents of an external directory in Swift 2. It seems easy enough for a local directory, but I just can't figure it out with an external directory. Here is my code:
var theUrlString = "https://great-castles.com/images/berkeley/"
var theUrl = NSURL(string: theUrlString)!
do {
let directoryContents = try NSFileManager.defaultManager().contentsOfDirectoryAtURL(theUrl, includingPropertiesForKeys: nil, options: NSDirectoryEnumerationOptions())
print(directoryContents)
} catch let error as NSError {
print(error.localizedDescription)
}
In the playground, I am getting the error:
The file “berkeley” couldn’t be opened because there is no such file
Note that I just threw a random URL in there as an example. But it is accessible, so I should be able to get a list of the files, right? Does NSFileManager.defaultManager think I am referring to a local file?
What I'd really like to do is copy the contents of an online directory into the document directory on the device. If the above isn't possible, any ideas on the best way to do that?