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

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
)

Related

What could be a reason for a do try catch issue with contentsOfFile in Swift 3?

I recently converted the syntax of a project I was working on from Swift 2.3 to Swift 3 and am running into some issues with taking the contents of a txt file and converting them into a string. Everything works fine on old txt files in my project but new txt files are failing in the try portion of my code.
I'm not super familiar with how try/catch works but I've tested the code and on files that don't work the catch portion of my code is running so I'm assuming that the try part is failing in those cases. Is this some sort of bug or something that changed in Swift 3? I've checked the new files and they are txt files, formatted exactly the same way as the working txt files so I'm not sure what is wrong.
if let filepath = Bundle.main.path(forResource: fileName, ofType: "txt") {
do {
contents = try String(contentsOfFile: filepath, encoding: String.Encoding.utf8)
print(contents)
textView.text = contents
} catch {
// contents could not be loaded
print(error)
}
} else {
print("No contents found")
}
Error Message with encoding parameter:
Error Domain=NSCocoaErrorDomain Code=261 "The file “35 IAC 14 - Indiana State Teachers' Retirement Fund.txt” couldn’t be opened using text encoding Unicode (UTF-8)." UserInfo={NSFilePath=/Users/graysonfaircloth/Library/Developer/CoreSimulator/Devices/(removed-numbers)/data/Containers/Bundle/Application/(removed-numbers)/CCHA-test-app.app/35 IAC 14 - Indiana State Teachers' Retirement Fund.txt, NSStringEncoding=4}
Error Message without encoding parameter:
Error Domain=NSCocoaErrorDomain Code=264 "The file “35 IAC 14 - Indiana State Teachers' Retirement Fund.txt” couldn’t be opened because the text encoding of its contents can’t be determined." UserInfo={NSFilePath=/Users/graysonfaircloth/Library/Developer/CoreSimulator/Devices/(removed-numbers)/data/Containers/Bundle/Application/(removed-numbers)/CCHA-test-app.app/35 IAC 14 - Indiana State Teachers' Retirement Fund.txt}
In Swift 3 we have extended using throwing, I think it's better then handling error.
You need to check is your file included into build
and check name of file.
Also you can print your path and check manually is your file added to build (simulator).

files saved as package (with filewrappers) and icloud drive, are they zipped when emailed?

I have an app which uses UIDocument and writes data using filewrappers:
public override func contents(forType typeName: String) throws -> Any {
print("******writing the contents******")
print("**** typeName = \(typeName)")
let result = FileWrapper(directoryWithFileWrappers: [:])
... lots more....
}
This actually works fine. I also have in my plist the uti of the document. The document can be saved, copied to icloud drive, imported back in to the app, so far so good....
But now the hard part: Icloud drive has an email button. If I mail a working file via that button to myself, and save the file on the mac, the right click menu option "show package contents" is gone. (While it is there for the same file in icloud drive).
Also, if i try to open this mail attachment on the iphone, my app is unable to find a subpath into the wrapped file. (so my file is called bla.myextension, and metadata is supposed to be at bla.myextension/meta.xml but the file which is in Inbox appears to be a "file" rather than a directory.
What is happening. Is the email somehow automatically zipping things up?
The file indeed is zipped automatically. So in application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) just check if the result is a directory, because opening from icloud drive may give you unzipped results. If it is a directory then you have to unzip it yourself.
var isDir : ObjCBool = ObjCBool( false)
if fileManager.fileExists(atPath: url.path, isDirectory: &isDir) {
if isDir.boolValue {
...copy directory...
}
else {
...unzip and copy....
}
}

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

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

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.

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?