How to unzip a .zip file on iOS? [duplicate] - iphone

This question already has answers here:
unzip source code in Iphone
(2 answers)
Is there any zip decompression for iPhone?
(3 answers)
Closed 9 years ago.
After StoreKit downloads the IAP content package it returns an NSURL to me which looks like this:
file://localhost/private/var/mobile/Applications/45EF2B3A-3CAB-5A44-4B4A-631A122A4299/Library/Caches/BA32BC55-55DD-3AA4-B4AC-C2A456622229.zip/
Despite all sources I found claiming that StoreKit unzips the content package once downloaded, it hands me over a ZIP. This ZIP probably contains the file structure of the content package. But how do I unzip this?

Use Zip Foundation if you are working in Swift language. It's easy to use and one of the best swift library for unzipping a zip file.
Zip:
let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var sourceURL = URL(fileURLWithPath: currentWorkingPath)
sourceURL.appendPathComponent("file.txt")
var destinationURL = URL(fileURLWithPath: currentWorkingPath)
destinationURL.appendPathComponent("archive.zip")
do {
try fileManager.zipItem(at: sourceURL, to: destinationURL)
} catch {
print("Creation of ZIP archive failed with error:\(error)")
}
UnZip:
let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var sourceURL = URL(fileURLWithPath: currentWorkingPath)
sourceURL.appendPathComponent("archive.zip")
var destinationURL = URL(fileURLWithPath: currentWorkingPath)
destinationURL.appendPathComponent("directory")
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)")
}
If you are using Objective-C then SSZipArchive is the best choice for this.
You can unzip using this
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *outputPath = [documentsDirectory stringByAppendingPathComponent:#"/ImagesFolder"];
NSString *zipPath = Your zip file path;
[SSZipArchive unzipFileAtPath:zipPath toDestination:outputPath delegate:self];
Hope it helps you.

There is a great 3rd party tool for zipping/unzipping files for iPhone
https://github.com/soffes/ssziparchive
Very simple to use. Hope that helps!!
Edit:
Quick method I created which takes url, downloads the zip and unzips it
-(void)downloadAndUnzip : (NSString *)sURL_p : (NSString *)sFolderName_p
{
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(q, ^{
//Path info
NSURL *url = [NSURL URLWithString:sURL_p];
NSData *data = [NSData dataWithContentsOfURL:url];
NSString *fileName = [[url path] lastPathComponent];
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
[data writeToFile:filePath atomically:YES];
dispatch_async(main, ^
{
//Write To
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:sFolderName_p];
[SSZipArchive unzipFileAtPath:filePath toDestination:dataPath];
});
});
}

Related

How do I save a UIImage to a file?

If I have a UIImage from an imagePicker, how can I save it to a subfolder in the documents directory?
Of course you can create subfolders in the documents folder of your app. You use NSFileManager to do that.
You use UIImagePNGRepresentation to convert your image to NSData and save that to disk.
// Create path.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Image.png"];
// Save image.
[UIImagePNGRepresentation(image) writeToFile:filePath atomically:YES];
Core Data has nothing to do with saving images to disk by the way.
In Swift 3:
// Create path.
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let filePath = "\(paths[0])/MyImageName.png"
// Save image.
UIImagePNGRepresentation(image)?.writeToFile(filePath, atomically: true)
You have to construct a representation of your image as a particular format (say, JPEG or PNG), and then call writeToFile:atomically: on the representation:
UIImage *image = ...;
NSString *path = ...;
[UIImageJPEGRepresentation(image, 1.0) writeToFile:path atomically:YES];
The above are useful, but they don't answer your question of how to save in a subdirectory or get the image from a UIImagePicker.
First, you must specify that your controller implements image picker's delegate, in either .m or .h code file, such as:
#interface CameraViewController () <UIImagePickerControllerDelegate>
#end
Then you implement the delegate's imagePickerController:didFinishPickingMediaWithInfo: method, which is where you can get the photograph from the image picker and save it (of course, you may have another class/object that handles the saving, but I'll just show the code inside the method):
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
// get the captured image
UIImage *image = (UIImage *)info[UIImagePickerControllerOriginalImage];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *imageSubdirectory = [documentsDirectory stringByAppendingPathComponent:#"MySubfolderName"];
NSString *filePath = [imageSubdirectory stringByAppendingPathComponent:#"MyImageName.png"];
// Convert UIImage object into NSData (a wrapper for a stream of bytes) formatted according to PNG spec
NSData *imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:filePath atomically:YES];
}
If you want to save as JPEG image, the last 3 lines would be:
NSString *filePath = [imageSubdirectory stringByAppendingPathComponent:#"MyImageName.jpg"];
// Convert UIImage object into NSData (a wrapper for a stream of bytes) formatted according to JPG spec
NSData *imageData = UIImageJPEGRepresentation(image, 0.85f); // quality level 85%
[imageData writeToFile:filePath atomically:YES];
extension UIImage {
/// Save PNG in the Documents directory
func save(_ name: String) {
let path: String = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let url = URL(fileURLWithPath: path).appendingPathComponent(name)
try! UIImagePNGRepresentation(self)?.write(to: url)
print("saved image at \(url)")
}
}
// Usage: Saves file in the Documents directory
image.save("climate_model_2017.png")
NSData *imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:path atomically:YES];
where path is the name of the file you want to write it to.
First you should get the Documents directory
/* create path to cache directory inside the application's Documents directory */
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"fileName"];
Then you should save the photo to the file
NSData *photoData = UIImageJPEGRepresentation(photoImage, 1);
[photoData writeToFile:filePath atomically:YES];
In Swift 4.2:
// Create path.
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
if let filePath = paths.first?.appendingPathComponent("MyImageName.png") {
// Save image.
do {
try image.pngData()?.write(to: filePath, options: .atomic)
} catch {
// Handle the error
}
}
In Swift 4:
// Create path.
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
if let filePath = paths.first?.appendingPathComponent("MyImageName.png") {
// Save image.
do {
try UIImagePNGRepresentation(image)?.write(to: filePath, options: .atomic)
}
catch {
// Handle the error
}
}

How to list all folders and their subdirectories/files in iPhone SDK?

I want the user to select any file present in her/his iPhone so that it’s used as an e-mail attachment. For this purpose, I want to show the list of all files and folders present in iPhone. But how would I get the list of those files? Is there any way to get that list?
Take into account that your app runs in a sandbox and would not be able to get any folder/file outside of that sandbox.
ObjectiveC
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *manager = [NSFileManager defaultManager];
NSArray *fileList = [manager contentsOfDirectoryAtPath:documentsDirectory error:nil];
for (NSString *s in fileList){
NSLog(#"%#", s);
}
Swift 4
guard let documentsDirectory = try? FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else { return }
guard let fileEnumerator = FileManager.default.enumerator(at: documentsDirectory, includingPropertiesForKeys: nil, options: FileManager.DirectoryEnumerationOptions()) else { return }
while let file = fileEnumerator.nextObject() {
print(file)
}
Here's the slowest(?) approach:
NSFileManager * fileManager = [NSFileManager new];
NSArray * subpaths = [fileManager subpathsAtPath:path];
but that should at least point you to a more specialized implementation for your needs.
Slightly lower level abstractions which allow you to enumerate on demand include NSDirectoryEnumerator and CFURLEnumerator. Depending on the depth of the directory, these have the potential to save much unnecessary interactions with the filesystem, compared to -[NSFileManager subpathsAtPath:].
You can use NSDirectoryEnumerator via NSFileManager.enumeratorAtPath
From the docs:
NSString *docsDir = [NSHomeDirectory() stringByAppendingPathComponent: #"Documents"];
NSFileManager *localFileManager=[[NSFileManager alloc] init];
NSDirectoryEnumerator *dirEnum =
[localFileManager enumeratorAtPath:docsDir];
NSString *file;
while ((file = [dirEnum nextObject])) {
if ([[file pathExtension] isEqualToString: #"doc"]) {
// process the document
[self scanDocument: [docsDir stringByAppendingPathComponent:file]];
}
}
swift 3
let fileManager:FileManager = FileManager()
let files = fileManager.enumerator(atPath: NSHomeDirectory())
while let file = files?.nextObject() {
print("Files::",file)
}
I'm an author of FileExplorer control which is a file browser for iOS and fulfills most of your requirements. Note that it allows you to browse only those files and directories that are placed inside your sandbox.
Here are some of the features of my control:
Possibility to choose files or/and directories if there is a need for that
Possiblity to remove files or/and directories if there is a need for that
Built-in search functionality
View Audio, Video, Image and PDF files.
Possibility to add support for any file type.
You can find my control here.

Read Image file from custom directory using NSBundle

Creating a custom directory which has all the images. Designing it custom because, it will help me to get the images as an when I need at various places in the configuration.
NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
[filemgr createDirectoryAtPath: #"/Users/home/lifemoveson/test" withIntermediateDirectories:YES attributes: nil error:NULL];
I have placed images under test folder and are of .png types.
Is there a way to retrieve the images like below.
/** UPDATED again **/
Currently this folder is under Application_Home/Resources/ImageTiles/ as per Photoscroller example.
How can we change it to /Users/home/lifemoveson/test/ImageTiles/ folder ?
- (UIImage *)tileForScale:(CGFloat)scale row:(int)row col:(int)col
{
// we use "imageWithContentsOfFile:" instead of "imageNamed:" here because we don't want UIImage to cache our tiles
NSString *tileName = [NSString stringWithFormat:#"%#_%d_%d_%d", imageName, (int)(scale * 1000), col, row];
// Currently this folder is under <Application_Home>/Resources/ImageTiles/ as per Photoscroller example.
// How can we change it to /Users/home/lifemoveson/test/ImageTiles/ folder ?
NSString *path = [[NSBundle mainBundle] pathForResource:tileName ofType:#"png"];
UIImage *image = [UIImage imageWithContentsOfFile:path];
return image;
}
Applications running on iOS are sandboxed; you can't simply create directories wherever you please. Your createDirectoryAtPath: call will fail. You should use one of the directories set aside for your application instead.
Once you obtain the path for one of those directories, getting the path for files within them is simply a case of using NSString's stringByAppendingPathComponent: method.
Makin a call to the functions such as
NSString* newDirPath = [self createDirectoryWithName:#"Test"];
if (newDirPath) {
[self saveFile:#"MasterDB.sqlite" atPath:newDirPath];
}
which are implemented as follows
-(NSString*)createDirectoryWithName:(NSString*)dirName{
NSArray* directoryArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask , YES);
NSString* directoryPath = [directoryArray objectAtIndex:0];
NSString* newPath = [directoryPath stringByAppendingString:[NSString stringWithFormat:#"/%#",dirName]];
NSFileManager *filemamager = [NSFileManager defaultManager];
BOOL flag = [filemamager createDirectoryAtPath:newPath withIntermediateDirectories:YES attributes: nil error:NULL];
return flag == YES ?newPath: nil;
}
-(BOOL)saveFile:(NSString*)fileName atPath:(NSString*)path{
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the File and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the File has already been created in the users filesystem
NSString *filePath = [path stringByAppendingPathComponent:fileName];
success = [fileManager fileExistsAtPath:filePath];
// If the File already exists then return without doing anything
if(success) return YES;
// If not then proceed to copy the File from the application to the users filesystem
// Get the path to the database in the application package
NSString *pathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:fileName];
// Copy the database from the package to the users filesystem
NSError *error = nil;
BOOL flag = [fileManager copyItemAtPath:pathFromApp toPath:filePath error:&error];
return flag;
}
might help you solve your problem. Creating a directory is achieved using first function and the second will let you save files from your application bundle to any of the previously created directory. I hope you can modify it save files located at places other than Application bundle to suit to your need.

Create a folder inside documents folder in iOS apps

I just want to create new folders in the documents folder of my iPhone app.
Does anybody know how to do that?
Appreciate your help!
I do that the following way:
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:#"/MyFolder"];
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder
I don't have enough reputation to comment on Manni's answer, but [paths objectAtIndex:0] is the standard way of getting the application's Documents Directory
http://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/StandardBehaviors/StandardBehaviors.html#//apple_ref/doc/uid/TP40007072-CH4-SW6
Because the
NSSearchPathForDirectoriesInDomains
function was designed originally for
Mac OS X, where there could be more
than one of each of these directories,
it returns an array of paths rather
than a single path. In iOS, the
resulting array should contain the
single path to the directory. Listing
3-1 shows a typical use of this
function.
Listing 3-1 Getting the path to the
application’s Documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
Swift 3 Solution:
private func createImagesFolder() {
// path to documents directory
let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
if let documentDirectoryPath = documentDirectoryPath {
// create the custom folder path
let imagesDirectoryPath = documentDirectoryPath.appending("/images")
let fileManager = FileManager.default
if !fileManager.fileExists(atPath: imagesDirectoryPath) {
do {
try fileManager.createDirectory(atPath: imagesDirectoryPath,
withIntermediateDirectories: false,
attributes: nil)
} catch {
print("Error creating images folder in documents dir: \(error)")
}
}
}
}
I don't like "[paths objectAtIndex:0]" because if Apple adds a new folder starting with "A", "B" oder "C", the "Documents"-folder isn't the first folder in the directory.
Better:
NSString *dataPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/MyFolder"];
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder
The Swift 2 solution:
let documentDirectoryPath: String = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first!
if !NSFileManager.defaultManager().fileExistsAtPath(documentDirectoryPath) {
do {
try NSFileManager.defaultManager().createDirectoryAtPath(documentDirectoryPath, withIntermediateDirectories: false, attributes: nil)
} catch let createDirectoryError as NSError {
print("Error with creating directory at path: \(createDirectoryError.localizedDescription)")
}
}
This works fine for me,
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *appSupportDir = [fm URLsForDirectory:NSDocumentsDirectory inDomains:NSUserDomainMask];
NSURL* dirPath = [[appSupportDir objectAtIndex:0] URLByAppendingPathComponent:#"YourFolderName"];
NSError* theError = nil; //error setting
if (![fm createDirectoryAtURL:dirPath withIntermediateDirectories:YES
attributes:nil error:&theError])
{
NSLog(#"not created");
}
Swift 4.0
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
// Get documents folder
let documentsDirectory: String = paths.first ?? ""
// Get your folder path
let dataPath = documentsDirectory + "/yourFolderName"
if !FileManager.default.fileExists(atPath: dataPath) {
// Creates that folder if not exists
try? FileManager.default.createDirectory(atPath: dataPath, withIntermediateDirectories: false, attributes: nil)
}
Following code may help in creating directory :
-(void) createDirectory : (NSString *) dirName {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Fetch path for document directory
dataPath = (NSMutableString *)[documentsDirectory stringByAppendingPathComponent:dirName];
NSError *error;
if (![[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]) {
NSLog(#"Couldn't create directory error: %#", error);
}
else {
NSLog(#"directory created!");
}
NSLog(#"dataPath : %# ",dataPath); // Path of folder created
}
Usage :
[self createDirectory:#"MyFolder"];
Result :
directory created!
dataPath : /var/mobile/Applications/BD4B5566-1F11-4723-B54C-F1D0B23CBC/Documents/MyFolder
Swift 1.2 and iOS 8
Create custom directory (name = "MyCustomData") inside the documents directory but only if the directory does not exist.
// path to documents directory
let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as! String
// create the custom folder path
let myCustomDataDirectoryPath = documentDirectoryPath.stringByAppendingPathComponent("/MyCustomData")
// check if directory does not exist
if NSFileManager.defaultManager().fileExistsAtPath(myCustomDataDirectoryPath) == false {
// create the directory
var createDirectoryError: NSError? = nil
NSFileManager.defaultManager().createDirectoryAtPath(myCustomDataDirectoryPath, withIntermediateDirectories: false, attributes: nil, error: &createDirectoryError)
// handle the error, you may call an exception
if createDirectoryError != nil {
println("Handle directory creation error...")
}
}

Copy folder from iPhone Resources directory to document directory

BOOL success;
NSFileManager *fileManager = [[NSFileManager defaultManager]autorelease];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *documentDBFolderPath = [documentsDirectory stringByAppendingPathComponent:#"DB"];
success = [fileManager fileExistsAtPath:documentDBFolderPath];
if (success){
return;
}else{
NSString *resourceDBFolderPath = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:#"DB"];
[fileManager createDirectoryAtPath: documentDBFolderPath attributes:nil];
[fileManager copyItemAtPath:resourceDBFolderPath toPath:documentDBFolderPath
error:&error];
}
}
Like this.
Resources/DB/words.csv => DB folder copy => Document/DB/words.csv
I want to copy DB subdirectory at Resources folder. I thought that source is good. But that source makes folder and doesn't copy files in DB folder at Resources folder.
I really want to copy files in DB folder at Resources folder. please help me.
1) Do not -autorelease the NSFileManager. You are double-releasing it which will crash your app.
2) No need to call -createDirectoryAtPath:. From the SDK doc of -copyItemAtPath:toPath:error:,
The file specified in srcPath must exist, while dstPath must not exist prior to the operation
and creating the directory the copy to fail.
Swift 3.0
Using String
func copyFolder(){
// Get the resource folder
if let resourceMainPath = Bundle.main.resourcePath{
var isDirectory = ObjCBool(true)
// Get the path of the folder to copy
let originPath = (resourceMainPath as NSString).appendingPathComponent("NameOfFolder")
// Get the destination path, here copying to Caches
let destinationPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first!
// Append the folder name to dest path so that system creates the directory if it doesnt exist
let destPath = (destinationPath as NSString).appendingPathComponent("/NameOfFolder")
let fileManager = FileManager.default
if fileManager.fileExists(atPath: destPath, isDirectory:&isDirectory ){
// If an overwrite behavior is needed, remove and copy again here
print("Exists")
}else{
// Do the copy
do {
try fileManager.copyItem(atPath: originPath, toPath: destPath)
}catch let error{
print(error.localizedDescription)
}
}
}else{
}
}
Using URL
func copyTheFolder(){
// Get the resource folder
if let resourceMainURL = Bundle.main.resourceURL{
var isDirectory = ObjCBool(true)
// Get the path of the folder to copy
let originPath = resourceMainURL.appendingPathComponent("NameOfFolder")
// Get the destination path, here copying to Caches
let destinationPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first!
// Append the folder name to dest path so that system creates the directory if it doesnt exist
let destURL = URL(fileURLWithPath: destinationPath).appendingPathComponent("/NameOfFolder")
let fileManager = FileManager.default
if fileManager.fileExists(atPath: destURL.path, isDirectory:&isDirectory ){
// If an overwrite behavior is needed, remove and copy again here
print("Exists")
}else{
// Do the copy
do {
try fileManager.copyItem(at: originPath, to: destURL)
}catch let error{
print(error.localizedDescription)
}
}
}
}