FileManager.copyItem(at: URL, to: URL) fails without throwing an error - swift

I've noticed that FileManager's methods for moving and copying items behave differently. fileManager.moveItem(at: src, to: dst) throws error if dst directory has immutable attribute:
Error Domain=NSCocoaErrorDomain Code=513 "“src” couldn’t be moved because you don’t have permission to access “dst”." UserInfo={NSSourceFilePathErrorKey=dst, NSUserStringVariant=(
Move
), NSDestinationFilePath=dst, NSFilePath=src, NSUnderlyingError=0x108b0e190 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
Instead fileMangaer.copyItem(at: src, to: dst) doesn't throw any errors. It's only output to console a message:
Open on dst: Operation not permitted
Is there are any way to copy file with better error handling?
Running on macOS 10.12.5
UPD:
Code example:
let dir = "/private/tmp/file-manager-test"
let file = "/private/tmp/file1"
do {
try FileManager.default.createDirectory(atPath: dir, withIntermediateDirectories: true, attributes: nil)
FileManager.default.createFile(atPath: file, contents: Data(), attributes: nil)
try FileManager.default.setAttributes([.immutable: true], ofItemAtPath: dir)
let fname = (file as NSString).lastPathComponent
let dst = (dir as NSString).appendingPathComponent(fname)
try FileManager.default.copyItem(atPath: file, toPath: dst)
} catch let e {
print("\(e)")
}
It works as expected in playground. But if you run this code in applicationDidFinishLaunching() method you will see no exceptions.
UPD2:
I've found similar case on OpenRadar: https://openradar.appspot.com/32083596
Looks like this is system's bug related to OS_ACTIVITY_DT_MODE environment variable.

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

ZipFoundation cannot unarchive the file because it can't be found

I'm trying to unzip some archives in a macOS app using the ZipFoundation library. The files are located on the desktop, I've confirmed the paths are correct and that the file exists.
I have this code:
let fileManager = FileManager()
let sourceURL = URL(fileURLWithPath: monetizeZiPFile.absoluteString)
var destinationURL = URL(fileURLWithPath: dirBuildURL.absoluteString)
destinationURL.appendPathComponent("directory")
do {
try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
try fileManager.unzipItem(at: monetizeZiPFile, to: dirBuildURL)
} catch {
print("Extraction of ZIP archive failed with error:\(error)")
}
I receive this error for the unzipItem method:
CFURLResourceIsReachable failed because it was passed an URL which has no scheme
Extraction of ZIP archive failed with error:Error Domain=NSCocoaErrorDomain Code=260
"The file “00-monetize.zip” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/Users/xxxxxxxx/Desktop/Componize-Builds/DOC-8621/Monetize/00-monetize.zip}
Do I have to be within the project directories for ZipFoundation to work?
For paths, do not use absoluteString, use path.

"Interrupted system call" when reading test data in XCTestCase

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.

Unzip the zip file saved into the app in xcode with swift 5.0

I have saved one zip file in my app, which is having .json files in it.
I have to unzip this zip file at some path and read the file after unzip.
I tried using package managers like ZipFoundation
If I run the app on my mac with OS 10.15.2 (macOS Catalina) I get the following output
"Extraction of ZIP archive failed with error:Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “directory” in the folder “Macintosh HD”." UserInfo={NSFilePath=/directory, NSUnderlyingError=0x600002f39530 {Error Domain=NSPOSIXErrorDomain Code=13 "Permission denied"}}"
if I run the app on device (iPhone XS Max iOS 13.3.1) I get the following error,
Extraction of ZIP archive failed with error:Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “directory” in the folder “System#snap-8290536”." UserInfo={NSFilePath=/directory, NSUnderlyingError=0x2811d54d0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
This is my code
import ZIPFoundation
func unarchiveFile(){
let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var sourceURL = URL(fileURLWithPath: "/Users/sagarchavan/Documents/Project/Swift-POC/iOS-CD-POCs/CDSwiftDemo/")
//var sourceURL = URL(fileURLWithPath:currentWorkingPath)
sourceURL.appendPathComponent("countryList.zip")
print("source url is :- :\(sourceURL)")
var destinationURL = URL(fileURLWithPath: currentWorkingPath)
destinationURL.appendPathComponent("directory")
print("destinationURL is :- :\(destinationURL)")
do {
try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
try fileManager.unzipItem(at: sourceURL, to: destinationURL)
} catch {
print("Extraction of ZIP archive failed with error:\(error)")
}
}
Can you please help me out to get out from this issue.
I need to unzip the zip file.
for sourceURL I tried both, by giving currentWorkingPath and by giving actual path to file. but both the time i get same error.
#note :- I don't want to use any pods or Carthage. I want default code or external dependancies are from package managers only.
Any help will be appreciated.
Thank you.
All Thanks to #vadian
I used URL(fileURLWithPath: currentWorkingPath)
Here is my sample code which is working well now,
func unarchiveFile(){
let fileManager = FileManager()
let currentWorkingPath = "/Users/sagarchavan/Documents/Project/Swift-POC/iOS-CD-POCs/CDSwiftDemo/CDSwiftDemo/Zipfile/"
var sourceURL = URL(fileURLWithPath:currentWorkingPath)
sourceURL.appendPathComponent("countryList.zip")
var destinationURL = URL(fileURLWithPath: currentWorkingPath)
destinationURL.appendPathComponent("unzipData")
do {
try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
try fileManager.unzipItem(at: sourceURL, to: destinationURL)
parseJSONFromFile(destinationPath: destinationURL)
} catch {
print("Extraction of ZIP archive failed with error:\(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: