"Interrupted system call" when reading test data in XCTestCase - swift

Using Xcode (11.4.1) on Catalina (10.15.4, only updated from Mojave 5 days ago), with a tiny SPM-based project opened in Xcode in "folder mode" (i.e. not an actual .xcodeproj), I have a test which should read some input data from a sample file. Following recommendations for constructing a URL to those files, Data(contentsOf:) cannot read it, although the generated URL is correct.
The code is:
func testCanReadConfigFromFile() {
let thisDirectory = URL(fileURLWithPath: #file)
.deletingLastPathComponent()
let url = thisDirectory
.appendingPathComponent("Test_data", isDirectory: true)
.appendingPathComponent("Config.json", isDirectory: false)
print(url.absoluteString)
do {
let _ = try Data(contentsOf: url)
} catch let error {
print(error)
print(error.localizedDescription)
}
}
The exception is:
Error Domain=NSCocoaErrorDomain Code=256 "The file “Config.json” couldn’t be opened."
UserInfo={NSFilePath=/full/path/to/project/Tests/ProjectTests/Test_data/Config.json,
NSUnderlyingError=0x100b0aa30
{Error Domain=NSPOSIXErrorDomain Code=4 "Interrupted system call"}}
If I take the string output by print(url.absoluteString) and in a terminal execute file <absoulteString>, it confirms that my file does exist at that path (and that it is JSON).
"Interrupted system call" makes me think Catalina's stricter sand-boxing rules are to blame, but I have not been shown a permissions dialog. This project is within my ~/Documents folder, which Xcode does have permission to read.
Edited to add:
swift test at the command line works. It's only running tests in Xcode that exhibits the problem.

Related

Permission denied in XCode debugging run

I want to read all files in a folder:
let fileManager = FileManager.default
let directoryEnumerator = fileManager.enumerator(at: self.url,
includingPropertiesForKeys: resourceKeys,
options: skipHiddenFiles ? [.skipsHiddenFiles] : [],
errorHandler: { (url, error) -> Bool in
print("directoryEnumerator error at \(url): ", error)
accessError = true
return true
})!
In that folder there are two files. But I get an Error, that I have not the permission to do this (to read it):
directoryEnumerator error at
file:///Users/Shared/MeinDesktop/fasttemp/tmp/src: Error
Domain=NSCocoaErrorDomain Code=257 "The file “src” couldn’t be opened
because you don’t have permission to view it."
UserInfo={NSURL=file:///Users/Shared/MeinDesktop/fasttemp/tmp/src,
NSFilePath=/Users/Shared/MeinDesktop/fasttemp/tmp/src,
NSUnderlyingError=0x600000a94240 {Error Domain=NSPOSIXErrorDomain
Code=1 "Operation not permitted"}}
I added Xcode itself as well as the generated app file to security section with full access. Nothing changed! The code worked for years, so I don't believe that this is wrong. Do I need some settings in XCode?
I got it:
There is a file in the project named "YourProjectName.entitlements".
Within that file there is a option to sandbox the app. Set this to "NO" and it works fine! Hurray

Access contents of SKDownload after processing in-app purchase using SKPaymentQueue

I've been trying to access a downloaded SKDownload zip file after a successful in-app purchase as such:
func paymentQueue(_ queue: SKPaymentQueue, updatedDownloads downloads: [SKDownload]) {
downloads.forEach ({ (download) -> Void in
switch download.state {
...
case .finished:
self.processDownload(download: download)
...
break
...
})
}
Here's the function that processes the downloaded SKDownload file:
func procesessDownload(download: SKDownload) {
guard let hostedContentPath = download.contentURL else {
return
}
do {
// THIS LINE OF CODE THROWS WITH THE ERROR POSTED BELOW
let files = try FileManager.default.contentsOfDirectory(atPath: hostedContentPath.relativePath)
} catch {
//catch error
}
}
When I inspect the download.contentURL it is there:
file:///private/var/mobile/Containers/Data/Application/D755EC3C-2BA6-43A8-BB21-938B38EBFCB7/Library/Caches/EB11461D-71C8-42C7-9C99-E6F8B81161EE.zip/
- _url : file:///private/var/mobile/Containers/Data/Application/D755EC3C-2BA6-43A8-BB21-938B38EBFCB7/Library/Caches/EB11461D-71C8-42C7-9C99-E6F8B81161EE.zip/
But upon trying to access the file using FileManager using this line of code above:
let files = try FileManager.default.contentsOfDirectory(atPath: hostedContentPath.relativePath)
I keep getting the following error stating that the file doesn't exist:
Error Domain=NSCocoaErrorDomain Code=260 "The file “EB11461D-71C8-42C7-9C99-E6F8B81161EE.zip” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/private/var/mobile/Containers/Data/Application/D755EC3C-2BA6-43A8-BB21-938B38EBFCB7/Library/Caches/EB11461D-71C8-42C7-9C99-E6F8B81161EE.zip, NSUnderlyingError=0x28320d500 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
I tried adding / at the end to treat the zip file as a directory and many other things to no avail.
I even tried to access the directory containing that file (up to the zip file):
/private/var/mobile/Containers/Data/Application/D755EC3C-2BA6-43A8-BB21-938B38EBFCB7/Library/Caches/
The error says that "Caches" directory not found.
I've also tried adding "Contents" to the path like one of the example codes I found by a random person.
I've been banging my head for more than a day straight and I couldn't find any documentation on the matter. Any help would be greatly appreciated, thanks!

How do I read a file from the filesystem in a Swift command line app?

I'm just starting learning Swift and to teach myself I'm making a simple command line app. It will eventually connect to an online data source but initially I want to load data from a file. I've seen various guides on reading the contents of a file in Swift but none of them seem to work for me. Here is my app so far:
import Foundation
// Set the file path
let path = "/Users⁩/username/workspace⁩/⁨Swift⁩/sis⁩/sis/data.json⁩"
do {
// Get the contents
let contents = try String(contentsOfFile: path, encoding: .utf8)
print(contents)
}
catch let error as NSError {
print("Ooops! Something went wrong: \(error)")
}
Running it outputs:
Ooops! Something went wrong: Error Domain=NSCocoaErrorDomain Code=260 "The file “data.json⁩” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/Users⁩/username/workspace⁩/⁨Swift⁩/sis⁩/sis/data.json⁩, NSUnderlyingError=0x100e19a50 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
However on the terminal:
$ ls -l /Users/username/workspace/Swift/sis/sis/data.json
-rwxrwxrwx# 1 username staff 165563 16 Jan 17:14 /Users/username/workspace/Swift/sis/sis/data.json
(yeah I relaxed the permissions somewhat just in case that was the problem)
The only slightly anomalous thing I noticed (aside from the inaccurate assertion that the file doesn't exist) was that when I copy and past the path from the XCode output into iTerm2 it puts spaces between each path component:
(pasted as an image as copying it and pasting it back into this form seems to hide the spaces - this is probably irrelevant anyway)
Any help figuring this out would be really appreciated!
I copied your code, downloaded a sample json file to my desktop, and renamed it to example_ 1.json (I included a space inside the file name).
import Foundation
// Set the file path
let path = "/Users⁩/username/Desktop/example_ 1.json⁩"
do {
// Get the contents
let contents = try String(contentsOfFile: path, encoding: .utf8)
print(contents)
}
catch let error as NSError {
print("Ooops! Something went wrong: \(error)")
}
It successfully printed the file. It also worked when I defined contents as a NSString.
let contents = try NSString(contentsOfFile: path,
encoding: String.Encoding.ascii.rawValue)
I am using Swift 4.2.1
you can not read if your command line app is sandboxed. what you can do is to add this file in your project and set path of file by looking the full path of file in identity inspector.
let path = "/Users/snx/EmailReplacer/EmailReplacer/shared_domains_staging.json"
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
if let jsonResult = jsonResult as? Dictionary<String, AnyObject> {
print(jsonResult)
}
} catch {
print(error)
}

How do I write to a local file in Swift/XCTest?

My ultimate question is about saving a screenshot from an AppleTV application using XCTest and Swift4 (running on a MacBook paired to the TV device), but I'm having trouble even writing a simple text string to a local file. If I can get this simple file-save working, I'm hoping I can resolve the screenshot issue. (Apologies for making this look like two questions but they appear to be related and resulted from my troubleshooting efforts.)
First, here's what I'm trying to do with a screenshot, based on sample code I found somewhere online:
let appshot = XCUIApplication().windows.firstMatch.screenshot()
let shotpath = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0].appendingPathComponent("appshot.png")
let shotpathUrl = URL(string: "file://\(shotpath)")
print("Saving to: \(shotpath)")
do {
try appshot.pngRepresentation.write(to: shotpathUrl!)
} catch {
print("Failed saving screenshot due to \(error)")
}
This gives me the following output:
Saving to: file:///var/mobile/Containers/Data/Application/77D52C66-353B-4029-97D5-48E6BAE35C92/Downloads/appshot.png
Failed saving screenshot due to Error Domain=NSCocoaErrorDomain Code=4 "The file “appshot.png” doesn’t exist." UserInfo={NSFilePath=///var/mobile/Containers/Data/Application/77D52C66-353B-4029-97D5-48E6BAE35C92/Downloads/appshot.png, NSUnderlyingError=0x1c405bc60 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Of course, the file doesn't exist because that's the file I'm trying to create. But /var/mobile doesn't exist on my laptop either -- it looks like the path FileManager is building may exist on the AppleTV device, but I want it on my laptop where my test script is executing.
So I backed out to a much more simple case, and even this is giving me problems:
let str = "This is a test"
let path = "file:///Users/haljor/foo.txt"
let pathUrl = URL(string: path)!
print("Path: \(path)")
print("URL: \(pathUrl)")
do {
try str.write(to: pathUrl, atomically: true, encoding: .utf8)
} catch {
print("Caught error writing to \(pathUrl): \(error)")
}
And here's the output:
Path: file:///Users/haljor/foo.txt
URL: file:///Users/haljor/foo.txt
Caught error writing to file:///Users/haljor/foo.txt: Error Domain=NSCocoaErrorDomain Code=4 "The folder “foo.txt” doesn’t exist." UserInfo={NSURL=file:///Users/haljor/foo.txt, NSUserStringVariant=Folder, NSUnderlyingError=0x1c40553f0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Here, it looks like it's trying to write to a folder at the path I specified, not a file. Clearly there's something I'm not understanding in each of these cases.
I don't really have a preference for whether I use a fully-specified path or something using FileManager -- it just needs to land somewhere on my laptop (not the TV device). What am I missing?
You can add an attachment to the test case and save it to disk too. The problem was that Downloads folder may not exist in the container yet. The best way to handle this is via init-once property:
var downloadsFolder: URL = {
let fm = FileManager.default
let folder = fm.urls(for: .downloadsDirectory, in: .userDomainMask)[0]
var isDirectory: ObjCBool = false
if !(fm.fileExists(atPath: folder.path, isDirectory: &isDirectory) && isDirectory.boolValue) {
try! fm.createDirectory(at: folder, withIntermediateDirectories: false, attributes: nil)
}
return folder
}()
func test() {
let appshot = XCUIScreen.main.screenshot()
let attachment = XCTAttachment(screenshot: appshot)
attachment.lifetime = .keepAlways
self.add(attachment)
// Save to container
let url = downloadsFolder.appendingPathComponent("appshot.png")
try! appshot.pngRepresentation.write(to: url)
}
If you want to view the attachment, right-click on the test case, select Jump to Report and expand the tree. You will see the screenshot eventually:

Swift 4 / Xcode 9.3 / iOS - Catch The file "x" couldn’t be opened because there is no such file with String(contentsOf: url)

Swift 4 / Xcode 9.3 / iOS
I'm using String(contentsOf: url) and in some cases I get an error because the file referenced by the URL doesn't exist. I want to handle that specific error case.
I know I can check to see if the file exists first, but I want to handle the error case instead.
Right now I am doing:
let data = try String(contentsOf: url)
Up until now I've been catching all errors, with something like:
do {
let data = try String(contentsOf: url)
//<other code here>
} catch let error {
fatalError("bad error: \(error)")
}
When the specific file I'm trying to pull from (from the give URL) doesn't exist, I get an error like this:
bad error: Error Domain=NSCocoaErrorDomain Code=260 "The file “whatever.json” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/Users/myname/Library/Developer/CoreSimulator/Devices/6529F22A-5234-49D7-9BB3-B9C71474CC53/data/Containers/Data/Application/005C1F55-07E8-4CF4-86FD-2F00B1144FD4/Documents/whatever.json, NSUnderlyingError=0x608000058180 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
How do I catch this error separately so I can handle it separately from other errors? I've tried CocoaError.fileNoSuchFile and several in the POSIX error domain.
Thanks!
Okay, so I've been fighting this for awhile, and as soon as I posted, I ended up solving it.
the answer is CocoaError.fileReadNoSuchFile. I kept trying CocoaError.fileNoSuchFile.
So here's the answer:
do {
let data = try String(contentsOf: url)
//<other code here>
} catch CocoaError.fileReadNoSuchFile {
print("CAUGHT IT!")
} catch let error {
fatalError("bad error: \(error)")
}
Thanks!