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

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....
}
}

Related

How to get list of files in iCloud container with swift?

After hours of hard work I could implement iCloud storage to my macOS app.
My container is displayed in iCloud. The name of the folder is correct. I can store generated pdf to all folders, but...
... I can't get a list of files of a certain folder inside this iCloud container.
I use this little snippet of code
let manager = FileManager.default
let driveURL = manager.url(forUbiquityContainerIdentifier: nil)?.appending(path: "Documents").appending(path:"Import")
try? manager.createDirectory(at: driveURL!, withIntermediateDirectories: true, attributes: [:])
let files = manager.enumerator(atPath: driveURL!.absoluteString)
files ist always empty (nil), because the folder Import doesn't exist (what's wrong, because it's there). And it should be created, if not exists.
Do I miss some settings to my project, to get access to my own folders? Or is there another, different way to get reading access?

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 :)

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 - 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?