non-persistence of object written to documentsDirectory - is - ios8

-- a question about how to make an object that is saved to the documents directory persist on the drive and be recoverable after the iDevice is rebooted.
Here's my problem. I make a data object with NSCoding and fill it with data. I write it to the documentsDirectory each time the data in the object are updated. I stop the app and start the app again, and my data object persists, with all of its data. But if I reboot the iPhone the code I wrote to recover and read the data object fails.
The code I wrote originally used only a NSString for the file path. It worked well under ios7 but it fails under ios8.
Reading up on things, I found this clue from the Apple documentation:
"Important: Although they are safe to use while your app is running, file reference URLs are not safe to store and reuse between launches of your app because a file’s ID may change if the system is rebooted. If you want to store the location of a file persistently between launches of your app, create a bookmark as described in Locating Files Using Bookmarks."
So I rewrote my ios7 file open and file close methods so they no longer use strings or urls but get their strings and urls from a bookmark that is saved using NSUserDefaults. Same problem: everything works fine so long as I do not power off the phone, but all is lost once I do. I am not able to solve this.
Here is my current series of steps. First I either determine (or if it already exists in NSUsrDefaults, I recover) the absolute path to the documentsDirectory, using a bookmark:
+ (NSString*) getGeoModelAbsolutePath
{
NSString *path;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSURL *documentsDirectoryBookmarkURL;
NSData* documentsDirectoryBookmark = [userDefaults objectForKey:#"documentDirectoryBookmark"];
if(documentsDirectoryBookmark == nil)
{
documentsDirectoryBookmarkURL = [self getDocumentsDirectoryURL];
documentsDirectoryBookmark = [self bookmarkForURL:documentsDirectoryBookmarkURL];
}
documentsDirectoryBookmarkURL = [self urlForBookmark:documentsDirectoryBookmark];
path = documentsDirectoryBookmarkURL.path;
path = [path stringByAppendingString:#"/Model.mod"];
return path;
}
using methods modified from my ios7 code (which used only the getDocumentsDirectory method):
+ (NSString *)getDocumentsDirectory
{
NSURL *directory = [self getDocumentsDirectoryURL];
NSString * documentsDirectory = directory.path;
return documentsDirectory;
}
And
+ (NSURL *)getDocumentsDirectoryURL
{
NSURL *directory = [[[NSFileManager defaultManager]
URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask]
lastObject];
return directory;
}
And
+ (NSData*)bookmarkForURL:(NSURL*)url {
NSError* theError = nil;
NSData* bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile
includingResourceValuesForKeys:nil
relativeToURL:nil
error:&theError];
if (theError || (bookmark == nil)) {
// Handle any errors.
return nil;
}
return bookmark;
}
So now I have a NSString path with the model filename that I can use to get to the GeoModel
- (GeoModel*) openGeoModel
{
GeoModel *geoModel;
NSString* documentsDirectoryGeoModel =[FileManager getGeoModelAbsolutePath];
if([FileManager fileExistsAtAbsolutePath:documentsDirectoryGeoModel])
{
NSData* data = [NSData dataWithContentsOfFile: documentsDirectoryGeoModel]; //]documentsDirectoryGeoModel];
geoModel = [NSKeyedUnarchiver unarchiveObjectWithData: data];
NSString *unarchivedGeoModelVersion = geoModel.geoModel_VersionID;
if(![unarchivedGeoModelVersion isEqual: currentGeoModelVersion])
{
[FileManager deleteFile:documentsDirectoryGeoModel];
geoModel = [GeoModel geoModelInit];
[Utilities setGeoProjectCounter:0];
}
}
else
{
geoModel = [GeoModel geoModelInit];
}
[FileManager saveGeoModel];
return geoModel;
}
Which I then can save to the documentsDirectory as follows:
+ (BOOL)saveGeoModel
{
NSError *error = nil;
NSString *path = [self getGeoModelAbsolutePath];
[NSKeyedArchiver archiveRootObject:appDelegate.currentGeoModel toFile:path];
NSData* encodedData = [NSKeyedArchiver archivedDataWithRootObject: appDelegate.currentGeoModel];
BOOL success = [encodedData writeToFile: path options:NSDataWritingAtomic error:&error];
return success;
}
Which is always successful -- but is persistent only if I do not turn off the device! I am not making any progress with this: Any help would be much appreciated!
Thanks in advance
Tim Redfield

There. I think it is answered -- unless someone else has a comment on how to improve the above listings, they DO work as they ought to!

Related

How to use hardcoded file path names with sandbox

Ok, yes I know now that you can not use hardcoded paths with sandbox. Up to this point I have not delt with sandbox, so I never encountered it.
I have a Coredata App (Mac OSx) and I used the default save code and the default path location (user/...../applicationsupport/... This, of coarse, is not acceptable in the sandbox.
Without requiring the user to manually open the data file each time the program is launched, is there another way to deal with this?
I would appreciate any input/suggestions.
Thanks You
Sandbox doesn't mean there isn't any access to files and folders without user selection. As it said in App Sandbox in Depth article there's container directory you still having access to.
For taking a path to your Application Support-directory you should use the same code whenever you use Sandboxing or not.
+ (NSString *)executableName
{
NSString *executableName = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleExecutable"];
if(!executableName || executableName.length==0)
return nil;
return executableName;
}
- (NSString *)findOrCreateDirectory:(NSSearchPathDirectory)searchPathDirectory
inDomain:(NSSearchPathDomainMask)domainMask
appendPathComponent:(NSString *)appendComponent
error:(NSError **)errorOut
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory,domainMask,YES);
if ([paths count]==0)
return nil;
NSString *resolvedPath = [paths objectAtIndex:0];
if (appendComponent)
resolvedPath = [resolvedPath stringByAppendingPathComponent:appendComponent];
NSError *error;
BOOL success = [self createDirectoryAtPath:resolvedPath withIntermediateDirectories:YES attributes:nil error:&error];
if (!success)
{
if (errorOut)
*errorOut = error;
return nil;
}
return resolvedPath;
}
- (NSString *)applicationSupportDirectory
{
NSError *error;
NSString *result = [self findOrCreateDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask
appendPathComponent:[self executableName] error:&error];
if (error)
return nil;
return result;
}

Cant save image locally IOS8

i have this code working on IOS7, its check if an image exist in device and if not, it download locally.
Now on IOS8 doesnt save nothing, could someone help me?
//folder where save
NSString *ImagesPath = [[NSString alloc] initWithString:[[[[NSBundle mainBundle] resourcePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:#"Documents"]];
// check if image exist
NSString* foofile = [ImagesPath stringByAppendingPathComponent:nombreImagenLocal];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:foofile];
// check if image exist locally
if (!fileExists){
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:rutaCompletaLogo]];
//if not, i save it
if (data) {
// url where is saved
NSString *cachedImagePath = [ImagesPath stringByAppendingPathComponent:nombreImagenLocal];
if ([data writeToFile:cachedImagePath atomically:YES]) {
NSLog(#"Downloaded file saved to: %#", cachedImagePath);
}// end
The path to 'Documents' folder has changed from iOS8. Check the Apple tech note
Please, make sure you don't use hardcoded values. Use the methods provided by the API:
NSString *resourcePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
OR (as stated in the previous link)
// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
Hope this helps!

Cannot record movie using AVCaptureMovieFileoutput

I trying to record a video and I'm getting the error like
Cannot record to URL <#file url> because it is not a file URL.
I define the destination url as follows:
NSString *Path = [[NSString alloc] init];
Path = #"/Users/me/Documents/My fols/recording_try/newMovie.mov";
NSURL *dest = [[NSURL alloc] initWithString:[Path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
Then after creating the session, input and output objects. I tried recording like this.
mMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init] ;
[mSession addOutput:mMovieFileOutput];
[mMovieFileOutput startRecordingToOutputFileURL:dest recordingDelegate:self];
I have started running the session, tried using begin and comitconfiguration, etc. But every time I run I get an error like:
[AVCaptureMovieFileOutput startRecordingToOutputFileURL:recordingDelegate:] - Cannot record to URL /Users/me/Documents/My%20fols/recording_try/newMovie.mov because it is not a file URL.
I don't know where I'm going wrong... Could someone please help???
Thanks in Advance...
Just change your NSURL configuration to conform to file URL type
Something like this:
NSURL *dest = [[NSURL alloc] initFileURLWithPath:<#(NSString *)#>]
Try something like this:
// variable names should start with lower case letters
// also, let's do as much as we can with auto-released objects
// so we don't have to worry about leaking (if we're not using ARC)
NSString *path = [NSString stringWithString: #"/Users/me/Documents/My fols/recording_try"];
NSURL *dest = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
if(dest == nil)
{
NSLog( #"does not appear to be a valid NSURL object" );
return;
}
NSError * error = nil;
if([[NSFileManager defaultManager] createDirectoryAtURL: dest withIntermediateDirectories: YES attributes: nil error: &error] == YES)
{
dest = [dest URLByAppendingPathComponent: #"newMovie.mov"];
// now you can create the session plus input and output objects
// within this block
} else {
NSLog( #"was not able to create the directory which contains path %# - error is %#", path, [error localizedDescription] );
}

How to download file to machine without storing information on iCloud?

How to download file without storing information on iCloud?
My App is the App for downloading pdf to the machine. It was rejected by Apple from the reason that "In particular, we found that after downloading two issues, your app stores 56.5 MB. To check how much data your app is storing: - Go to Settings> iCloud. > Storage & Backup> Manage Storage ". I would like to know how to write if the information is not stored on iClound.
My Script :
- (void) DownloadFile:(NSURL *)url Name:(NSString *)name Folder:(NSString *)folder{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSData *myData = [NSData dataWithContentsOfURL:url];
NSString *file = [NSString stringWithFormat:#"%#/%#",folder,name];
if(![fileManger fileExistsAtPath:file]){
[fileManger createFileAtPath:file contents:myData attributes:nil];
}
}
Just store the PDFs in the application document directory. You can still keep track of which files have been downloaded on which devices by using the iCloud's key-value store. However, the files to be downloaded onto the device would have to come from non-iCloud servers.
You will probably also have to implement a UI for showing which files have been downloaded and which are available and for deleting them from the device if desired.
OH Thank you it complete
#import <sys/xattr.h>
....
-(BOOL) addSkipBackupAttributeToItemAtURL:(NSURL *)URL{
if(&NSURLIsExcludeFromBackupKey == nil){
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
}else{
return [URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}
- (void) DownloadFile:(NSURL *)url Name:(NSString *)name Folder:(NSString *)folder{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *curfile = [NSURL fileURLWithPath:folder];
[self addSkipBackupAttributeToItemAtURL:curfile];
NSData *myData = [NSData dataWithContentsOfURL:url];
NSString *file = [NSString stringWithFormat:#"%#/%#",folder,name];
if(![fileManger fileExistsAtPath:file]){
[fileManger createFileAtPath:file contents:myData attributes:nil];
}
}
Thank you

Cocoa: Getting image file metadata

I am building an app that allows users to select a file and/or folder either locally or across the network and list the contents of that selection in a NSTableView after some filtering (no hidden files, only accepting .tif, .eps). The user can then select a file name from the list and then have the files metadata shown to them. At least that is what I want to happen. Right now I am getting null returned for the metadata. Here's my code:
- (void)tableViewSelectionDidChange:(NSNotification *)notif {
NSDictionary* metadata = [[NSDictionary alloc] init];
//get selected item
NSString* rowData = [fileList objectAtIndex:[tblFileList selectedRow]];
//set path to file selected
NSString* filePath = [NSString stringWithFormat:#"%#/%#", objPath, rowData];
//declare a file manager
NSFileManager* fileManager = [[NSFileManager alloc] init];
//check to see if the file exists
if ([fileManager fileExistsAtPath:filePath] == YES) {
//escape all the garbage in the string
NSString *percentEscapedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)filePath, NULL, NULL, kCFStringEncodingUTF8);
//convert path to NSURL
NSURL* filePathURL = [[NSURL alloc] initFileURLWithPath:percentEscapedString];
NSError* error;
NSLog(#"%#", [filePathURL checkResourceIsReachableAndReturnError:error]);
//declare a cg source reference
CGImageSourceRef sourceRef;
//set the cg source references to the image by passign its url path
sourceRef = CGImageSourceCreateWithURL((CFURLRef)filePathURL, NULL);
//set a dictionary with the image metadata from the source reference
metadata = (NSDictionary *)CGImageSourceCopyPropertiesAtIndex(sourceRef,0,NULL);
NSLog(#"%#", metadata);
[filePathURL release];
} else {
[self showAlert:#"I cannot find this file."];
}
[fileManager release];
}
I'm guessing the problem here is the CFURLREF in CGImageSourceCreateWithURL. Instead of NSURL should I be using something else?
Thanks
Here's the path I am passing (logged from filePathURL): file://localhost/Volumes/STORAGE%20SVR/Illustration-Wofford/Illustration%20Pickup/Archive/AL013111_IL_Communication.eps
I can't tell where the "localhost" part in your file URL comes from, but I think that's the culprit. A file url doesn't usually contain a "localhost" part. In your example, it should look like this:
file:///Volumes/STORAGE%20SVR/Illustration-Wofford/Illustration%20Pickup/Archive/AL013111_IL_Communication.eps
But I'm pretty sure you've figured this out by now :)
Update: I stand corrected by Mike's comment: file://localhost/... is the same thing as file:///...!

Resources