My app downloads at start a sqlite database about 300MB. After the download is done, this sqlite database should be available during the installtime of the app (years :-)) . I don't know where to store this files.
I started to store the data in the library path as follow (swift)
var sDataPath = NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true);
No I realize that this path changes during development and testing with the IOS Simulator. Example:
1. I start the app the first time in OIS Simulator. The Evaluated Path is
[/Users/user/Library/Developer/CoreSimulator/Devices/19E2CB7E-3ABB-4C0A-8B49-39A0BE392A93/data/Containers/Data/Application/5EE51B55-0A89-45FB-A1E2-9BE3DCD33463/Library]
2. The App downloads the data. This takes some minutes.
3. I close the simulator and restart also xcode
4. I start the app again in the simulator and expect to have the already downloaded data in my apps' library path. But no ...
5. the app evaluates a new path, which has another ID in the Path:
[/Users/user/Library/Developer/CoreSimulator/Devices/19E2CB7E-3ABB-4C0A-8B49-39A0BE392A93/data/Containers/Data/Application/429206E9-00EA-45EF-BE6E-4B2E9374BAF5/Library]
6. And the app downloads the content again.
I would like to have a static path over the lifetime of the device. What am I doing wrong or what should I change ?
The path is changing on the simulator but it will be the same on a real device.
If I'm not wrong, on the simulator the path change every time you restart it. On a device the path will change only if you delete and reinstall your application.
You should save downloaded image to document directory. In this way images can load fastly. If u want to overcome of reloading cell then you have to check for then file path whether is fill is there or not.
- (NSString *)documentsPathForFileName:(NSString *)name folder:(NSString*)folderName{
return [[self pathToPatientPhotoFolder:folderName] stringByAppendingPathComponent:name];
}
- (NSString *)pathToPatientPhotoFolder:(NSString *)folderName {
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES) lastObject];
NSString *patientPhotoFolder = [documentsDirectory stringByAppendingPathComponent:folderName];
// Create the folder if necessary
BOOL isDir = NO;
NSFileManager *fileManager = [[NSFileManager alloc] init];
if (![fileManager fileExistsAtPath:patientPhotoFolder
isDirectory:&isDir] && isDir == NO) {
[fileManager createDirectoryAtPath:patientPhotoFolder
withIntermediateDirectories:NO
attributes:nil
error:nil];
}
return patientPhotoFolder;
}
in your cellForRowAtIndexPath: method
{…..
NSURL *urla = [NSURL URLWithString:[NSString stringWithFormat:#"%#",[self.imagearray objectAtIndex:indexPath.item]]];
NSString *thumbnailCacheKey = [NSString stringWithFormat:#"thumb-%#",[self.imageIDarray objectAtIndex:indexPath.item]];;
UIImage *image = [UIImage imageWithContentsOfFile:[self documentsPathForFileName:thumbnailCacheKey folder:#"thumb"]];
if (!image){
//first download image here and then save that image like:
NSString *stringPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]stringByAppendingPathComponent:#"thumb"];
// New Folder is your folder name
NSError *error = nil;
if (![[NSFileManager defaultManager] fileExistsAtPath:stringPath])
[[NSFileManager defaultManager] createDirectoryAtPath:stringPath withIntermediateDirectories:NO attributes:nil error:&error];
NSString *filePath = [stringPath stringByAppendingPathComponent:thumbnailCacheKey];
NSData *pngData = UIImageJPEGRepresentation(image, 0.8);
[pngData writeToFile:filePath atomically:YES];
}
else{// image is already downloaded, so just set that image
cell.imageView.image = image;
}
….
}
This code creates folder in your DocumentDirectory and save image there and fetch from there. I save with one unique photoID so that image can not be same. You can save with your requirement.
You should store it on Cache folder.
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *cacheFile = [cachesPath stringByAppendingPathComponent:#"file.plist"];
Store large file in other directory will be rejected by apple app review team (as i have faced)
Related
I am uploading a password protected zip file as a backup to iCloud, it gets uploaded and then I download, unzip and use the same file, it works perfectly fine only if we are doing all these cool stuffs from the same device.
Consider this scenario,
1. zip and upload the file from one device.
2. download and unzip it from other device (Cannot unzip as it does not recognize that file is in zip format)
Note:
I have cross verified that file gets successfully downloaded in other device by enabling my iTunes file sharing.
But I am not able to unzip the file.
I found the solution by myself hope this will save someone else time, converting password protected zip file into data and uploading, downloading, unzipping the same
Any file can be uploaded to iCloud container of any size (yes you should be having that much of space in iCloud) lets take an example SampleData.zip
// 1 This method will upload or sync SampleData.zip file in iCloud container, iCloud actually checks the metadata of your file before it uploads it into your iCloud container (so for first time it will upload the file and from next time it will only upload the changes)
-(void) iCloudSyncing:(id)sender
{
//Doc dir
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"SampleData.zip"];
NSURL *u = [[NSURL alloc] initFileURLWithPath:filePath];
NSData *data = [[NSData alloc] initWithContentsOfURL:u];
//Get iCloud container URL
NSURL *ubiq = [[NSFileManager defaultManager]URLForUbiquityContainerIdentifier:nil];// in place of nil you can add your container name
//Create Document dir in iCloud container and upload/sync SampleData.zip
NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:#"Documents"]URLByAppendingPathComponent:#"SampleData.zip"];
Mydoc = [[MyDocument alloc] initWithFileURL:ubiquitousPackage];
Mydoc.zipDataContent = data;
[Mydoc saveToURL:[Mydoc fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success)
{
if (success)
{
NSLog(#"SampleData.zip: Synced with icloud");
}
else
NSLog(#"SampleData.zip: Syncing FAILED with icloud");
}];
}
// 2 Download data from the iCloud Container
- (IBAction)GetData:(id)sender {
//--------------------------Get data back from iCloud -----------------------------//
id token = [[NSFileManager defaultManager] ubiquityIdentityToken];
if (token == nil)
{
NSLog(#"ICloud Is not LogIn");
}
else
{
NSLog(#"ICloud Is LogIn");
NSError *error = nil;
NSURL *ubiq = [[NSFileManager defaultManager]URLForUbiquityContainerIdentifier:nil];// in place of nil you can add your container name
NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:#"Documents"]URLByAppendingPathComponent:#"SampleData.zip"];
BOOL isFileDounloaded = [[NSFileManager defaultManager]startDownloadingUbiquitousItemAtURL:ubiquitousPackage error:&error];
if (isFileDounloaded) {
NSLog(#"%d",isFileDounloaded);
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
//changing the file name as SampleData.zip is already present in doc directory which we have used for upload
NSString* fileName = [NSString stringWithFormat:#"RecSampleData.zip"];
NSString* fileAtPath = [documentsDirectory stringByAppendingPathComponent:fileName];
NSData *dataFile = [NSData dataWithContentsOfURL:ubiquitousPackage];
BOOL fileStatus = [dataFile writeToFile:fileAtPath atomically:NO];
if (fileStatus) {
NSLog(#"success");
}
}
else{
NSLog(#"%d",isFileDounloaded);
}
}
}
//3 voila its done :)
I am saving video/image in document directory.Now once the image is saved in document directory I want to save its reference in my local database.So I am thinking I can save URL of the image in the local database.
So is it constant throughout my app?
It's not constant, i have observed every time you launch the app it'll be different, but your data is moved to this new path. You can save your file name in your database, and dynamically append this file name to NSDocument directory.
- (NSString *)documentsFilePath:(NSString *)fileName {
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths firstObject];
NSString *filePath = [docsDir stringByAppendingPathComponent:fileName];
return filePath;
}
- (void)storeFile:(NSString *)fileName {
NSString *filePath = [self documentsFilePath:fileName];
// create if needed
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
// Write your data to file system here...
}
}
- (void)deleteFile:(NSString *)fileName {
NSString *filePath = [self documentsFilePath:fileName];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSError *deleteErr = nil;
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&deleteErr];
if (deleteErr) {
NSLog(#"Can't delete %#: %#", filePath, deleteErr);
}
}
}
Please handle nil checks and store only filename in DB
No, it's not constant. Whenever your app reinstall or updated on device the document directory will change, because when app installed on device os made an directory for app with some random id and each install this random it get changed by OS.
So, you need to make it dynamic own your own, like store the file name only and append the document directory path while using it.
I would suggest only saving the filename or subdirectory/filename (if you have a subdirectory) in the database and then only attaching that to the NSDocumentDirectory.
This will ensure that you always know where the file is...
NSDocumentDirectory is however consistent accross updates, so the files should remain in the document directory even if you update...
I've got an app which saves images (JPEGs) and text files in the app's documents directory. I have tested it in the xcode simulator and on two iPhones (4 and 5, running ios 7.1 and 8.1 respectively), and it works exactly as expected, and the data is preserved fine.
However after submitting it to the app store a user testing the app (using iphone 6, ios 8.1) has found that the saved data is being lost every 20 minutes or so. Does anyone know why this is and how I can solve the issue?
Would marking the files as Do Not Backup solve the issue?
For reference, data being saved in NSUserDefaults is being preserved.
Thanks in advance.
EDIT----
I should have mentioned that I am searching for the documents directory by using:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
self.docsPath = [paths objectAtIndex:0];
I am then appending self.docsPath with the string attributed to the image/file, XXXX.jpg:
self.imgPath = [self.fileName stringByAppendingString:#".jpg"];
self.tempPhotoPath = [self.docsPath stringByAppendingPathComponent:self.imgPath];
I should also clarify that the app functions normally on the iPhone 4/5, unplugged from xcode and running appstore downloaded versions of the app. So far in investigating, it is specifically the iPhone 6 that I am having the problems with, and from what I can tell it is simply clearing the documents directory every 20 minutes or so.
in Apple new documents, whenever app launches every time it generate new sandbox id. So, it you have saved image with full path then it will might lost in second app launch. It will be not showing this effect if you test on simulator or device connected to xcode. Just plug out and run ur app, you can also see this issue your self.
So, instead of save image with its path, just create a folder in Document directory and save your image at there. For path generate you can write below code: (But make sure you save image with a specific id, like imageID and then fetch same image from that imageID).
- (NSString *)documentsPathForFileName:(NSString *)name folder:(NSString*)folderName{
return [[self pathToPatientPhotoFolder:folderName] stringByAppendingPathComponent:name];
}
- (NSString *)pathToPatientPhotoFolder:(NSString *)folderName {
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES) lastObject];
NSString *patientPhotoFolder = [documentsDirectory stringByAppendingPathComponent:folderName];
// Create the folder if necessary
BOOL isDir = NO;
NSFileManager *fileManager = [[NSFileManager alloc] init];
if (![fileManager fileExistsAtPath:patientPhotoFolder
isDirectory:&isDir] && isDir == NO) {
[fileManager createDirectoryAtPath:patientPhotoFolder
withIntermediateDirectories:NO
attributes:nil
error:nil];
}
return patientPhotoFolder;
}
NSURL *urla = [NSURL URLWithString:[NSString stringWithFormat:#"%#",[self.imagearray objectAtIndex:indexPath.item]]];// imagearray for array of image url from server
NSString *thumbnailCacheKey = [NSString stringWithFormat:#"thumb-%#",[self.imageIDarray objectAtIndex:indexPath.item]];//first try to check thumb-<my image id> is exist or not.
UIImage *image = [UIImage imageWithContentsOfFile:[self documentsPathForFileName:thumbnailCacheKey folder:#"thumb"]];//this will check thumb-<my image id> in ur sandbox
if (!image){
UIImage *imageToSave = //
NSString *stringPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]stringByAppendingPathComponent:#"thumb"];
// New Folder is your folder name
NSError *error = nil;
if (![[NSFileManager defaultManager] fileExistsAtPath:stringPath])
[[NSFileManager defaultManager] createDirectoryAtPath:stringPath withIntermediateDirectories:NO attributes:nil error:&error];
NSString *filePath = [stringPath stringByAppendingPathComponent:thumbnailCacheKey];
NSData *pngData = UIImageJPEGRepresentation(image, 0.8);
[pngData writeToFile:filePath atomically:YES];
}
else{
yourimageView.image = image;
}
I have a bunch of images stored in an images directory within my Supported Files directory in Xcode. I want to be able to show one of those images. What is the best way to obtain a path to that image? Do I have to copy them to the Documents directory first? If so, how can I do that?
EDIT: I've tried the following to copy the image from Supporting Files to the Documents folder in the app. It successfully copies, but I can't get the image to show:
-(void)findImage:(NSString *)imageName
{
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appImagePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.jpg",imageName]];
success = [fileManager fileExistsAtPath:appImagePath];
if (success)
{
return;
}
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultImagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.jpg",imageName]];
success = [fileManager copyItemAtPath:defaultImagePath toPath:appImagePath error:&error];
if (!success)
{
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
self.imageDisplay.image = [UIImage imageNamed:appImagePath];
return;
}
This should do the trick:
[UIImage imageNamed:#"someImageName"];
EDIT:
Some additional information:
-imageNamed: will look through the entire main bundle of the application for an imagefile (preferrably an png) with the filename of "someImageName". You need not worry about its location or its extension, since it will be searched for in the mainbundle. Files that you import through the import-file-dialogue in xcode will be added to he main bundle.
This means:
If i have imported a file called myImage.png, calling [UIImage imageNamed:#"myImage"];from anywhere in my code will get me a UIImage-Object containing that image. Its amazingly simple, and maybe that startled you a bit ;)
Look it up in the docs if you like:
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIImage_Class/Reference/Reference.html
I have a UIViewController with an UIWebView which displays a pdf file depending which row was clicked before in an UITableView. Now I want to add a button for the user to save this pdf file locally for offline use.
Then there is a second UITableView which should display the name of the saved pdf and by clicking on it another UIViewController appears and displays the saved pdf on a UIWebView offline.
What would be a good way to start?
Thanks
You can try this way:
1) Add a button to the View containing UIWebView
2) At button press save the file shown in UIWebView
(note: in iOS 5 you must save data that can be easily recreated or downloaded to the caches directory)
- (IBAction)buttonPress:(id)sender
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
BOOL isDir = NO;
NSError *error;
//You must check if this directory exist every time
if (! [[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&isDir] && isDir == NO)
{
[[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:&error];
}
NSString *filePath = [cachePath stringByAppendingPathComponent:#"someName.pdf"]
//webView.request.URL contains current URL of UIWebView, don't forget to set outlet for it
NSData *pdfFile = [NSData dataWithContentsOfURL:webView.request.URL];
[pdfFile writeToFile:filePath atomically:YES];
}
3) On application start you need to check what files are stored (iOS can delete cache directory if there is not enough space on iPhone)