Swift 2.0 - Unable to Write to Text File (OS X) - swift

I have a text file that I will be reading and writing to. I have gotten the code to read successfully, but am unable to write to the text file again. I want to overwrite everything (not amend).
if let dir : NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first {
let path = dir.stringByAppendingPathComponent("airports.txt")
var strTextToWrite:String = String()
strTextToWrite = "Test"
do {
try strTextToWrite.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding)
}
catch { print("Error") }
}
The above code doesn't throw an error (i.e. the word 'Error' isn't printed as per my catch code), but the text file remains unedited, retaining the value it had before the code was run.
The text file is contained inside the X-Code project as shown below:
I'm at a real loss here, I don't understand why the code isn't working. I have confirmed that it is being run (by printing the value of strTextToWrite before and after the do statement, and it is definitely running through the do block of code because it isn't being caught by the catch block. The only thing I can think of is that the code is pointing at the wrong file (but the file name is correct, as you can verify with the screenshot above).
What am I doing wrong? Thanks in advance.

You are looking at 2 different files: the one you write to is ~/Documents/airport.txt while the one you see (and check) in Xcode is located in your project folder. To see the update, you need to reference the file in Xcode, not make a copy of it.
Right click airport.txt in your Project Navigator and click Delete > Move to Trash
Go to ~/Documents/airport.csv and drag it into your Xcode project. Do not add it to any target and unselect "Copy Items if Necessary"
You now have created a link to the file.
Modifying files in your bundle is generally a bad idea. If you need a place to output a temporary file, use the app's subfolder under ~/Application Support or the system's temp folder.

Related

How do you zip a directory in Swift without compressing one of the files it contains to make an ePub file?

I am trying to programmatically create an ePub file. I am following this tutorial. The tutorial says to "make the .epub container that all these files go in" by:
Create an empty .zip file with whatever name you like (See notes below for detailed instructions on how to do this.)
Copy the mimetype file into the zip file (don't use compression on this file)
Copy the rest of the files and folders mentioned above into the zip file *
Re-name the .zip extension to .epub
These are the last few steps of the process. I have all the files that need to go into the zip ready. I know the files work because I used a third party program (eCanCrusherMac.1.2.1) to do these finals steps and the product it creates is an ePub file that loads in the Books eReader (made by Apple).
I used the below code to zip the desired directory. I found this code on here Stack Overflow
func zip(itemAtURL itemURL: URL, in destinationFolderURL: URL, zipName: String) throws {
var error: NSError?
var internalError: NSError?
NSFileCoordinator().coordinate(readingItemAt: itemURL, options: [.forUploading], error: &error) { (zipUrl) in
// zipUrl points to the zip file created by the coordinator
// zipUrl is valid only until the end of this block, so we move the file to a temporary folder
let finalUrl = destinationFolderURL.appendingPathComponent(zipName)
do {
try FileManager.default.moveItem(at: zipUrl, to: finalUrl)
} catch let localError {
internalError = localError as NSError
}
}
if let error = error {
throw error
}
if let internalError = internalError {
throw internalError
}
}
I took the file this function gives me, made sure it had the epub extension and tried to open it using the Books app but it fails to load. The code produces a zip file that I can interact with normally in Finder so I know the function works.
I believe the issue is with the "mimetype" file getting compressed. I have taken a valid ePub, changed the file's extension to zip, unzipped it and then rezipped it using Finder and tried to open it again with no other changes and it doesn't work. As you can see in the instructions from the tutorial at the top of this post the "mimetype" file can't be compressed.
This is a Swift app on Mac.
I looked into the different NSFileCoordinator.ReadingOptions and NSFileCoordinator.WritingOptions and searched on Stack Overflow and elsewhere online but I can't find anything on how to create a zip file in Swift without compressing a file contained in the zip file.
I was able to get the ePub to open using ZIPFoundation:
try FileManager.default.zipItem(
at: bookURL,
to: ePubURL,
shouldKeepParent: false,
compressionMethod: .none,
progress: nil
)

Swift: How to unzip file not contained in my bundle?

I am not very familiar with Swift programming but I need to write a small tool in Swift which can unzip a file (and then launch a program). I need to unzip a file which is not contained in my app bundle. It is located in /Users/me/folder1/folder2/openjdk-11.0.2.zip
I tried the libraries "Zip", "ZipFoundation", and "SSZipArchive". From what I read so far, I think that the libraries which I tried need the zip file to be located in the app bundle but I am not sure.
With "Zip" I tried:
_ = try Zip.quickUnzipFile(URL(string: openjdkZipUrl!.relativePath)!)
With "ZipFoundation" I tried:
let fileManager = FileManager()
let archive = openjdkZipUrl
let destinationURL = openjdkFolderUrl
do {
try fileManager.unzipItem(at: archive.url, to: destinationURL)
} catch {
}
ZipFoundation told me "Value of type 'FileManager' has no member 'unzipItem'" but I imported it with import Foundation. I also have it (and the other libraries) in my Podfile.
With "SSZipArchive" I tried:
let success = SSZipArchive.unzipFile(atPath: openjdkZipUrl!.path, toDestination: openjdkFolderUrl!.path)
The used paths are
let openjdkZip = "file:///Users/" + user + "/folder1/folder2/openjdk-11.0.2.zip"
let openjdkZipUrl = URL(string: openjdkZip)
and
let openjdkFolder = "file:///Users/" + user + "/folder1/folder2/openjdk-11.0.2"
let openjdkFolderUrl = URL(string: openjdkFolder)
Is it really a problem that the zip file is not contained in my bundle? Can someone tell me what I did wrong?
Thanks in advance
#trojanfoe mentioned in the comments:
Mac apps are sandboxed by default which means they have no access to a user's files unless they ask for it by getting the user to open the file/folder. You should ensure it's turned off for your app, however you are testing if the file exists and if this is succeeding then it looks like sandboxing is not turned on?
I looked at the .entitlements file in my project and found out that "App Sandbox" was set to "YES". I set it to "NO" and it worked perfectly.
It seems that you can check for a file while in sandbox mode (as I did with my condition) but not access them when trying to unzip them.
Thanks again #trojanfoe :)

Xcode Project Folder Structure

I am using the latest XCode / Swift 3.0 and facing some problems:
I created a folder in /MyProject/MyProject/Assets/Subfolder1 and stored a few hundred .txt files in it to iterate over the files inside the Subfolder1 with the following code:
let fm = FileManager.default
fm.enumerator(atPath: "/MyProject/Assets/Subfolder1")?.forEach({ (e) in
if let e = e as? String, let url = URL(string: e) {
doSomethingWith(url.pathExtension)
}
})
But I cannot find any files. I tried some variations ("/Assets/Subfolder1/" and "/MyProject/MyProject/Assets/Subfolder1"), all are not working.
What is the correct path that I have to specify?
Best
Chris
First off, make sure you have added the actual folder to the Xcode project. You can do this simply by dragging and dropping the folder into your project. After you drag and drop, this window should appear
Make sure you select Create folder references and Copy items if needed. This will make sure that the entire folder is constantly synced inside your Xcode project. Then to verify that the assets will be transferred to your device when building the project, go into Build Phases and then to Copy Bundle Resources and you should see the name of the folder, in my case, I have named it TextFiles.
Now because you have added your folder into the application's Main Bundle, you can access it by doing this:
let fm = FileManager.default
let path = Bundle.main.resourcePath?.appending("/TextFiles")
fm.enumerator(atPath: path!)?.forEach({ (e) in
if let e = e as? String, let url = URL(string: e) {
doSomethingWith(url.pathExtension)
}
})
The reason we need to do the Bundle.main.resourcePath before it and can't simply say /TextFiles is because if we say /TextFiles it looks for that folder starting from the system root, which is why it wasn't working before. It would be like having a folder in C:/Users/Abc123/Documents/TextFiles but the system is looking for it in C:/TextFiles.

Where do files go when you delete them programatically with swift?

Where do files go when you delete them programatically? I deleted them with this code, but the Trash for today is empty. Is it possible to retrieve them?
let filemgr = NSFileManager.defaultManager()
do{
let filelist = try filemgr.contentsOfDirectoryAtPath(fontFolderPath)
for filename in filelist {
do{ try filemgr.removeItemAtPath(fontFolderPath+filename)} catch{}
}
}catch{}
Using the URL related API of NSFileManager you have two options:
func removeItemAtURL(_ URL: NSURL) throws
deletes the item immediately like /bin/rm in Terminal.app and has the same functionality as removeItemAtPath.
func trashItemAtURL(_ url: NSURL,
resultingItemURL outResultingURL: AutoreleasingUnsafeMutablePointer<NSURL?>) throws
moves the item to the trash folder returning the item’s location in the trash via the inout pointer.
The removeItemAtPath method deletes them. They're gone. If you want to move something to the trash, you need to use NSWorkSpace. You can see an example of moving an entire directory to the trash here: Move directory to trash
basically unixy systems manage files the same way, there is a ref count, that is the number of hard links + times the file is open... So you can rm an open file, and the file will still exist, you can write to it, and read from it, with a valid file descriptor or FILE * stream object, then when it is closed the file will actually be removed from the disk...
int fd = open("somefile", O_RDWR);
unlink("somefile"); // removes somefile from the directory listing, but not disk
write(fd, "hello", 5);
lseek(fd,0,SEEK_SET); // seek to start of file
char buffer[6] = {0};
read(fd,buffer,5); // reads in "hello"
close(fd); // last reference removed, file is removed.
if you want to move a file to the Trash, that is a different operation and specific to OS X and iOS

How to delete a file while the application is running

I have developed a C# application, in the application the users choose a photo for each record. However the user should also be able to change the pre-selected photo with a newer one. When the user changes the photo the application first deletes the old photo from the application directory then copies the new photo, but when it does that the application gives an exception because the file is used by the application so it cannot be deleted while the application is running. Does any one have a clue how to sort this out? I appreciate your help
This is the exception
The process cannot access the file
'D:\My
Projects\Hawkar'sProject\Software\Application\bin\Debug\Photos\John
Smith.png' because it is being used by
another process.
//defining a string where contains the file source path
string fileSource = Open.FileName;
//defining a string where it contains the file name
string fileName = personNameTextBox.Text + ".png" ;
//defining a string which specifies the directory of the destination file
string fileDest = dir + #"\Photos\" + fileName;
if (File.Exists(fileDest))
{
File.Delete(fileDest);
//this is a picturebox for showing the images
pbxPersonal.Image = Image.FromFile(dir + #"\Photos\" + "No Image.gif");
File.Copy(fileSource, fileDest);
}
else
{
File.Copy(fileSource, fileDest);
}
imageIDTextBox.Text = fileDest;
First of all, you code is not good.
The new image is only copied if there is currently no image (else).
But if there is an old image, you only delete this image, but never copy the newer one (if).
The code should better look like this:
if (File.Exists(fileDest))
{
File.Delete(fileDest);
}
File.Copy(fileSource, fileDest);
imageIDTextBox.Text = fileDest;
This code should work, but if you are getting an exception that the file is already in use, you should check "where" you use the file. Perhaps you are reading the file at program start. Check all parts of your program you are accessing these user files if there are some handles open.
Thanks a lot for your help and sorry for the mistake in the code I just saw it, my original code is just like as you have written but I don't know maybe when I posted accidentally I've put like this. when the application runs there is a picturebox which the image of each record is shown, that is why the application is giving an exception when I want to change the picture because it has been used once by the picturebox , however I've also tried to load another picture to the picture box before deleting the original one but still the same. I've modified the above could if you want to examine it