Can't permanently save audio with AVAudioRecorder - iphone

An app i'm making has several views, each view has a record, stop and play button. The idea is that the user can record to a different sound file for each view.
I can record and playback a sound on each view but when i navigate away from the view and then navigate back the sound is gone.
I'm sorry to include so much code below but it's something i need to get to the bottom of.
Delegate.h
#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioServices.h>
#import <AVFoundation/AVFoundation.h>
#interface humptyDumptyAppDelegate : UIResponder <UIApplicationDelegate>
{
NSArray *dirPaths;
NSString *docsDir;
NSString *soundFilePathPage1;
NSString *soundFilePathPage2;
NSString *soundFilePathPage3;
NSString *soundFilePathPage4;
NSString *soundFilePathPage5;
NSString *soundFilePathPage6;
}
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) AVAudioRecorder *audioRecorder;
#property (strong, nonatomic) AVAudioPlayer *audioPlayer;
//example getter and setter functions
- (NSArray*) getDirPaths;
- (void) setDirPaths:(NSArray*)myDirPath;
- (NSString*) getDocsDir;
- (NSString*) soundFilePathForPageNumber:(int)pageNumber;
#end
Delegate.m
#import "humptyDumptyAppDelegate.h"
#implementation humptyDumptyAppDelegate
#synthesize window = _window;
#synthesize audioPlayer;
#synthesize audioRecorder;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
soundFilePathPage1 = [docsDir
stringByAppendingPathComponent:#"audiopage1.caf"];
soundFilePathPage2 = [docsDir
stringByAppendingPathComponent:#"page2.caf"];
soundFilePathPage3 = [docsDir
stringByAppendingPathComponent:#"page3.caf"];
soundFilePathPage4 = [docsDir
stringByAppendingPathComponent:#"page4.caf"];
soundFilePathPage5 = [docsDir
stringByAppendingPathComponent:#"page5.caf"];
soundFilePathPage6 = [docsDir
stringByAppendingPathComponent:#"page6.caf"];
return YES;
}
//getter function
- (NSArray*) getDirPaths{
return dirPaths;
}
//setter function
- (void) setDirPaths:(NSArray*)myDirPath{
dirPaths = myDirPath;
}
// get docs directory
-(NSString*) getDocsDir{
return docsDir;
}
// get sound file for page, passing the page number as an argument
-(NSString*) soundFilePathForPageNumber:(int)pageNumber{
switch (pageNumber) {
case 1:
return soundFilePathPage1;
break;
case 2:
return soundFilePathPage2;
break;
case 3:
return soundFilePathPage3;
break;
case 4:
return soundFilePathPage4;
break;
case 5:
return soundFilePathPage5;
break;
case 6:
return soundFilePathPage6;
break;
}
return nil;
}
page1.m
//this is called in viewDidLoad
-(void) prepareForAudioRecording
{
btnPlay.enabled = NO;
btnStop.enabled = NO;
int page = 1;
NSString *audioFilePath = [appDelegate soundFilePathForPageNumber:page];
NSURL *soundFileURL = [NSURL fileURLWithPath:audioFilePath];
NSError *error;
NSDictionary *recordSettings = [NSDictionary
dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:AVAudioQualityMin],
AVEncoderAudioQualityKey,
[NSNumber numberWithInt:16],
AVEncoderBitRateKey,
[NSNumber numberWithInt: 2],
AVNumberOfChannelsKey,
[NSNumber numberWithFloat:44100.0],
AVSampleRateKey,
nil];
appDelegate.audioRecorder = [[AVAudioRecorder alloc]
initWithURL:soundFileURL
settings:recordSettings
error:&error];
if (error)
{
NSLog(#"error: %#", [error localizedDescription]);
} else {
[appDelegate.audioRecorder prepareToRecord];
}
}
- (IBAction)recordAudio:(id)sender {
if (!appDelegate.audioRecorder.recording)
{
btnPlay.enabled = NO;
btnStop.enabled = YES;
[appDelegate.audioRecorder record];
}
}
- (IBAction)stopAudio:(id)sender {
btnStop.enabled = NO;
btnPlay.enabled = YES;
btnRecord.enabled = YES;
if (appDelegate.audioRecorder.recording)
{
[appDelegate.audioRecorder stop];
[self audioRecorderDidFinishRecording:appDelegate.audioRecorder successfully:YES];
} else if (appDelegate.audioPlayer.playing) {
[appDelegate.audioPlayer stop];
}
}
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
if (flag == YES){
NSLog(#"finished recording");
[appDelegate.audioPlayer.data writeToFile:[appDelegate soundFilePathForPageNumber:1] atomically:YES];
}
}
Like i said, i'm sorry for the amount of code included, but i'm unsure where the problem is. I'm calling the writeToFile method in the audioRecorderDidFinishRecording: method. I don't know if this is correct but i have a feeling that this is not the root of the problem.
Please help!!

this code saves to an audio file
what about copping the file to the documents directory
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#" sound.caf"];
success = [fileManager fileExistsAtPath:writableDBPath];
if (!success){
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"sound.caf"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
NSError *attributesError;
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:writableDBPath error:&attributesError];
NSNumber *fileSizeNumber = [fileAttributes objectForKey:NSFileSize];
long long fileSize = [fileSizeNumber longLongValue];
NSLog(#"file size: %lld",fileSize);
if (!success) {
NSLog(#"Failed to create writable database file with message: %#", [error localizedDescription]);
}
}

It turns out the problem was that in one of my pages i had my prepareForAudioRecording in viewDidAppear which was automatically overwriting the saved audio. Moving it to viewDidLoad did the trick

Related

can't create sqlite3 database in ios

Create Database Code
This code is correct. i used that code to create a database it's successful run and created a database but i test my application so i remove or delete my simulator app and again run my application then i see database is not created.
Please tell me what is the actual problem i face.
ClsUpdateNetworkViewController.h
#import <UIKit/UIKit.h>
#import "Person.h"
#import <sqlite3.h>
#interface ClsUpdateNetworkViewController : UIViewController{
UITextField *name;
UITextField *phone;
NSString *databasePath;
sqlite3 *contactDB;
int rowcount;
}
#property (nonatomic, strong) Person *person;
#property (nonatomic, strong) NSString *firstName;
#property (nonatomic, strong) NSString *lastName;
#property (nonatomic, strong) NSString *fullName;
#property (nonatomic, strong) NSString *phoneNumber;
#property (nonatomic, strong) NSString *workEmail;
#end
ClsUpdateNetworkViewController.m
#import "ClsUpdateNetworkViewController.h"
#import "ClsMainPageAppDelegate.h"
#import "ClsAddressBookViewController.h"
#interface ClsUpdateNetworkViewController ()
#end
#implementation ClsUpdateNetworkViewController
#synthesize person,firstName,lastName,fullName,phoneNumber,workEmail;
#synthesize name,phone;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self createDatabase];
}
-(void) createDatabase
{
NSString *docsDir;
NSArray *dirPaths;
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the database file
databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"contacts.db"]];
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: databasePath ] == NO)
{
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
char *errMsg;
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS CONTACTS (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, PHONE TEXT)";
if (sqlite3_exec(contactDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table");
}
sqlite3_close(contactDB);
}
else
{
NSLog(#"Failed to open/create database");
}
}
}
i test or debug my application
Code is not going inside this condition.
if ([filemgr fileExistsAtPath: databasePath ] == NO)
{
// inside code is not executed.
}
Call this Function at didFinishLaunchingWithOptions:
-(void)createdb
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dbPath = [documentsDirectory stringByAppendingPathComponent:#"emp.sqlite"];
NSURL *dbURL = [NSURL fileURLWithPath:dbPath];
// copy a database from the bundle if not present
// on disk
NSFileManager *fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:dbPath])
{
NSURL *bundlePath = [[NSBundle mainBundle] URLForResource:#"emp" withExtension:#"sqlite"];
NSError *error = nil;
if (!bundlePath || ![fm copyItemAtURL:bundlePath toURL:dbURL error:&error]) {
NSLog(#"error copying database from bundle: %#", error);
}
}
else
{
NSLog(#"Success in Copy file");
}
}
hi try this to create database only just add this code to your file
NSString *databaseName=#"Database.sqlite";
-(void)createDatabaseIfNeeded
{
// NSLog(#"Virtual database is created...");
NSFileManager *fileManager=[[NSFileManager defaultManager]autorelease];
NSError *error;
NSArray *paths= NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path=[paths objectAtIndex:0];
NSString *finalPath=[path stringByAppendingPathComponent:databaseName];
// NSLog(#"Database Path is:--> %#",finalPath);
NSString *bundlePath=[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
success = [fileManager fileExistsAtPath:finalPath];
if(success)
{
if(sqlite3_open([finalPath UTF8String],&database)!=SQLITE_OK)
{
sqlite3_close(database);
}
return;
}
success=[fileManager copyItemAtPath:bundlePath toPath:finalPath error:&error];
if(!success)
{
NSAssert1(0,#"Failed to create writable database file with message '%#' .",[error localizedDescription]);
}
else//First Time Loaded Application....
{
if(sqlite3_open([finalPath UTF8String],&database)!=SQLITE_OK)
{
sqlite3_close(database);
}
}
}
How it worked for me is this
//-----------------------------------------------------------------------------------------------------//
#pragma mark - Shared Instance
//-----------------------------------------------------------------------------------------------------//
+ (SQLConnector *)database
{
static SQLConnector *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [[SQLConnector alloc]init];
});
return _sharedClient;
}
//-----------------------------------------------------------------------------------------------------//
#pragma mark - Initialization Methods
//-----------------------------------------------------------------------------------------------------//
- (id)init {
if ((self = [super init]))
{
_databaseName = DB_NAME;
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
_databasePath = [documentsDir stringByAppendingPathComponent:_databaseName];
NSLog(#"%#",_databasePath);
if (sqlite3_open([[self dbPath] UTF8String], &_database) != SQLITE_OK)
{
[[[UIAlertView alloc]initWithTitle:#"Missing"
message:#"Database file not found"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil]show];
}
}
return self;
}
- (NSString *)dbPath
{
[self checkAndCreateDatabase];
return _databasePath;
}
-(void) checkAndCreateDatabase
{
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
success = [fileManager fileExistsAtPath:_databasePath];
// If the database already exists then return without doing anything
if(success) return;
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:_databaseName];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:_databasePath error:nil];
}
//-----------------------------------------------------------------------------------------------------//
#pragma mark - Helper methods
//-----------------------------------------------------------------------------------------------------//
-(BOOL)dbOpenedSuccessfully
{
if(sqlite3_open([[self dbPath] UTF8String], &_database) == SQLITE_OK)
{
return YES;
}
else
{
[[[UIAlertView alloc]initWithTitle:#"Error"
message:#"Error on opening the DB"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil]show];
return NO;
}
}
//-----------------------------------------------------------------------------------------------------//
#pragma mark - Query
//-----------------------------------------------------------------------------------------------------//
- (void) executeQuery:(NSString *)strQuery
{
char *error = NULL;
if([self dbOpenedSuccessfully])
{
NSLog(#"%#",strQuery);
sqlite3_exec(_database, [strQuery UTF8String], NULL, NULL,&error);
if (error!=nil) {
NSLog(#"%s",error);
}
sqlite3_close(_database);
}
}

PDF opens on the iPhone, but not on the iPad

I have an HTML document with a link that runs JavaScript code:
<a href="http://www.tricedesigns.com/temp/drm.pdf" onclick="app.openExternalDoc();">Open pdf</button>
<script type="text/javascript" src="../assets/js/index.js"></script>
<script type="text/javascript" src="../assets/js/ExternalFileUtil.js"></script>
<script type="text/javascript">
app.initialize();
</script>
<script src="../cordova.js"></script>
The index.js All this works fine
As does the ExternalFileUtil.js.
Here is the CDVExternalFileUtil.h file:
#import <Cordova/CDV.h>
#interface CDVExternalFileUtil : CDVPlugin <UIDocumentInteractionControllerDelegate> {
NSString *localFile;
}
- (void) openWith:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) cleanupTempFile: (UIDocumentInteractionController *) controller;
#end
And here is the CDVExternalFileUtil.m file:
#import "CDVExternalFileUtil.h"
#implementation CDVExternalFileUtil
- (void) openWith:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
CDVPluginResult* pluginResult;
NSString* callbackID = [arguments pop];
[callbackID retain];
NSString *path = [arguments objectAtIndex:0];
[path retain];
NSString *uti = [arguments objectAtIndex:1];
[uti retain];
NSLog(#"path %#, uti:%#", path, uti);
NSArray *parts = [path componentsSeparatedByString:#"/"];
NSString *previewDocumentFileName = [parts lastObject];
NSLog(#"The file name is %#", previewDocumentFileName);
NSData *fileRemote = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:path]];
// Write file to the Documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
if (!documentsDirectory) {NSLog(#"Documents directory not found!");}
localFile = [documentsDirectory stringByAppendingPathComponent:previewDocumentFileName];
[localFile retain];
[fileRemote writeToFile:localFile atomically:YES];
NSLog(#"Resource file '%#' has been written to the Documents directory from online", previewDocumentFileName);
The problem seems to be around this area (as it works on iPhone, but not iPad so I assume its something to do with the layout, Here is another similar question
// Get file again from Documents directory
NSURL *fileURL = [NSURL fileURLWithPath:localFile];
UIDocumentInteractionController *controller = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
controller.delegate = self;
controller.UTI = uti;
[controller retain];
CDVViewController* cont = (CDVViewController*)[ super viewController ];
CGRect rect = CGRectMake(0, 0, cont.view.bounds.size.width, cont.view.bounds.size.height);
[controller presentOptionsMenuFromRect:rect inView:cont.view animated:YES];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: #""];
[self writeJavascript: [pluginResult toSuccessCallbackString:callbackID]];
[callbackID release];
[path release];
[uti release];
}
- (void) documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *)controller {
NSLog(#"documentInteractionControllerDidDismissOpenInMenu");
[self cleanupTempFile:controller];
}
- (void) documentInteractionController: (UIDocumentInteractionController *) controller didEndSendingToApplication: (NSString *) application {
NSLog(#"didEndSendingToApplication: %#", application);
[self cleanupTempFile:controller];
}
- (void) cleanupTempFile: (UIDocumentInteractionController *) controller
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
BOOL fileExists = [fileManager fileExistsAtPath:localFile];
NSLog(#"Path to file: %#", localFile);
NSLog(#"File exists: %d", fileExists);
NSLog(#"Is deletable file at path: %d", [fileManager isDeletableFileAtPath:localFile]);
if (fileExists)
{
BOOL success = [fileManager removeItemAtPath:localFile error:&error];
if (!success) NSLog(#"Error: %#", [error localizedDescription]);
}
[localFile release];
[controller release];
}
#end
Sorry for the large amount of code, I have been stuck on this View/Download PDF for 5 days now, Any help would be great

how to maintain a plist so you dont lose your values

I have a plist which I read from my bundle into a new plist object that I put in the root directory for reading and writing. My question is what do I do with this or what dose the application do when its quit, and better yet what happens when the app is killed from the "multi task" menu from ios.
Also is there a way to save this plist to memory/the bundle for future use when the application is used again.
My code is as follows for refrence.
Here is my .h
#import <Foundation/Foundation.h>
#interface EngineProperties : NSObject {
NSString *signature;
NSNumber *version;
NSNumber *request;
NSNumber *dataVersion;
NSMutableDictionary *cacheValue;
//cachevalue Items
NSNumber *man;
NSNumber *mod;
NSNumber *sub;
}
#property (copy, nonatomic) NSString *signature;
#property (copy, nonatomic) NSNumber *version;
#property (copy, nonatomic) NSNumber *request;
#property (copy, nonatomic) NSNumber *dataVersion;
#property (copy, nonatomic) NSMutableDictionary *cacheValue;
//cachevalue Items
#property (copy, nonatomic) NSNumber *man;
#property (copy, nonatomic) NSNumber *mod;
#property (copy, nonatomic) NSNumber *sub;
//Singletton
+ (id)sharedManager;
- (void) saveData:(NSString *)methodName signature:(NSString *)pSignature Version:(NSNumber *)pVersion request:(NSNumber *)rNumber dataVersion:(NSNumber *)dvReturned cacheValue:(NSNumber *)cValue;
#end
and Here is my .m
#import "EngineProperties.h"
static EnginePropertiesController *sharedMyManager = nil;
#implementation EngineProperties
#synthesize signature;
#synthesize version;
#synthesize request;
#synthesize dataVersion;
#synthesize cacheValue;
#synthesize man;
#synthesize mod;
#synthesize sub;
#pragma mark Singleton Methods
+ (id)sharedManager {
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
// Data.plist code
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
// check to see if Data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// if not in documents, get property list from main bundle
plistPath = [[NSBundle mainBundle] pathForResource:#"EngineProperties" ofType:#"plist"];
}
// read property list into memory as an NSData object
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSPropertyListFormat format;
// convert static property liost into dictionary object
NSDictionary *tempRoot = (NSMutableDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
if (!tempRoot)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
// assign values
self.signature = [tempRoot objectForKey:#"Signature"];
self.version = [tempRoot objectForKey:#"Version"];
self.request = [tempRoot objectForKey:#"Request"];
self.dataVersion = [tempRoot objectForKey:#"Data Version"];
man = [cacheValue objectForKey:#"Man"];
mod = [cacheValue objectForKey:#"Mod"];
sub = [cacheValue objectForKey:#"SubMod"];
cacheValue = [tempRoot objectForKey:#"Cache Value"];
}
- (void) saveData:(NSString *)methodName signature:(NSString *)pSignature Version:(NSNumber *)pVersion request:(NSNumber *)rNumber dataVersion:(NSNumber *)dvReturned cacheValue:(NSNumber *)cValue;
{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
// set the variables to the values in the text fields
self.signature = pSignature;
self.version = pVersion;
self.request = rNumber;
self.dataVersion = dvReturned;
//do some if statment stuff here to put the cache in the right place or what have you.
if (methodName == #"manufacturers")
{
self.man = cValue;
}
else if (methodName == #"models")
{
self.mod = cValue;
}
else if (methodName == #"subMod")
{
self.sub = cValue;
}
self.cacheValue = [NSDictionary dictionaryWithObjectsAndKeys:
man, #"Manufacturers",
mod, #"Models",
sub, #"SubModels", nil];
NSDictionary *plistDict = [NSDictionary dictionaryWithObjectsAndKeys:
signature, #"Signature",
version, #"Version",
request, #"Request",
dataVersion, #"Data Version",
cacheValue, #"Cache Value", nil];
NSString *error = nil;
// create NSData from dictionary
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
// check is plistData exists
if(plistData)
{
// write plistData to our Data.plist file
[plistData writeToFile:plistPath atomically:YES];
NSString *myString = [[NSString alloc] initWithData:plistData encoding:NSUTF8StringEncoding];
// NSLog(#"%#", myString);
}
else
{
NSLog(#"Error in saveData: %#", error);
// [error release];
}
}
#end
It honestly depends on how frequently you'll be asking for and changing the plist's values. For instance, my app only needed to retrieve it once, then write it a few times (nothing much), so all of my saving code was at the end of said particular method.
However, if your plist is live (constant value changes), keeping a reference to the data you wish to save that is accessible from the AppDelegate is recommended. That way, you can simply call: beginBackgroundTaskWithExpirationHandler: on -applicationDidResignActive and save your plist data.
(Note, if the user is fast enough to kill your app before it saves completely (big if), there are no guarantees as to the integrity of your plist).
follow the blow link in that link they provided the code also. How to Write/Read data to .plist file.
Plist
You can quickly and easily save plists to NSUserDefaults
Check out this quick tutorial:
http://www.cocoadev.com/index.pl?NSUserDefaults

Variables losing their values before I can save them to plist

I am writing a controller class for my plist, which gets called from another class. the controller class has a read and save method, the read method opens up the plist and saves it to the documents file for reading and writing too. In this read class I put all of the values that have been saved previously into their own variables.
Then in the Save method call, which is called from another class, all the values are passed in as parameters then from there I pass the new values into the correct variables and pass that into the plist.. however I have a dictionary inside my plist and its only saving one value and the resetting the others to null.. I am wondering how do I make sure these variables keep their values?
Here is my .h
#import <Foundation/Foundation.h>
#interface EngineProperties : NSObject {
NSString *signature;
NSNumber *version;
NSNumber *request;
NSNumber *dataVersion;
NSMutableDictionary *cacheValue;
//cachevalue Items
NSNumber *man;
NSNumber *mod;
NSNumber *sub;
}
#property (copy, nonatomic) NSString *signature;
#property (copy, nonatomic) NSNumber *version;
#property (copy, nonatomic) NSNumber *request;
#property (copy, nonatomic) NSNumber *dataVersion;
#property (copy, nonatomic) NSMutableDictionary *cacheValue;
//cachevalue Items
#property (copy, nonatomic) NSNumber *man;
#property (copy, nonatomic) NSNumber *mod;
#property (copy, nonatomic) NSNumber *sub;
- (void) readPlistData;
- (void) saveData:(NSString *)methodName signature:(NSString *)pSignature Version:(NSNumber *)pVersion request:(NSNumber *)rNumber dataVersion:(NSNumber *)dvReturned cacheValue:(NSNumber *)cValue;
#end
and Here is my .m
#import "EngineProperties.h"
#implementation EngineProperties
#synthesize signature;
#synthesize version;
#synthesize request;
#synthesize dataVersion;
#synthesize cacheValue;
#synthesize man;
#synthesize mod;
#synthesize sub;
//This opens up and reads the current plist ready to be used
-(void) readPlistData
{
// Data.plist code
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
// check to see if Data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// if not in documents, get property list from main bundle
plistPath = [[NSBundle mainBundle] pathForResource:#"EngineProperties" ofType:#"plist"];
}
// read property list into memory as an NSData object
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSPropertyListFormat format;
// convert static property liost into dictionary object
NSDictionary *tempRoot = (NSMutableDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
if (!tempRoot)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
// assign values
self.signature = [tempRoot objectForKey:#"Signature"];
self.version = [tempRoot objectForKey:#"Version"];
self.request = [tempRoot objectForKey:#"Request"];
self.dataVersion = [tempRoot objectForKey:#"Data Version"];
man = [cacheValue objectForKey:#"Man"];
mod = [cacheValue objectForKey:#"Mod"];
sub = [cacheValue objectForKey:#"SubMod"];
cacheValue = [tempRoot objectForKey:#"Cache Value"];
}
- (void) saveData:(NSString *)methodName signature:(NSString *)pSignature Version:(NSNumber *)pVersion request:(NSNumber *)rNumber dataVersion:(NSNumber *)dvReturned cacheValue:(NSNumber *)cValue;
{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
// set the variables to the values in the text fields
self.signature = pSignature;
self.version = pVersion;
self.request = rNumber;
self.dataVersion = dvReturned;
//do some if statment stuff here to put the cache in the right place or what have you.
if (methodName == #"manufacturers")
{
self.man = cValue;
}
else if (methodName == #"models")
{
self.mod = cValue;
}
else if (methodName == #"subMod")
{
self.sub = cValue;
}
self.cacheValue = [NSDictionary dictionaryWithObjectsAndKeys:
man, #"Manufacturers",
mod, #"Models",
sub, #"SubModels", nil];
NSDictionary *plistDict = [NSDictionary dictionaryWithObjectsAndKeys:
signature, #"Signature",
version, #"Version",
request, #"Request",
dataVersion, #"Data Version",
cacheValue, #"Cache Value", nil];
NSString *error = nil;
// create NSData from dictionary
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
// check is plistData exists
if(plistData)
{
// write plistData to our Data.plist file
[plistData writeToFile:plistPath atomically:YES];
NSString *myString = [[NSString alloc] initWithData:plistData encoding:NSUTF8StringEncoding];
// NSLog(#"%#", myString);
}
else
{
NSLog(#"Error in saveData: %#", error);
// [error release];
}
}
#end
I am trying to find out how to keep the correct values in these three vars, man, mod, sub so I can put them into my cache value dictionary..
The problem is when you are getting your data ready to create the dictionary, here:
if (methodName == #"manufacturers")
{
self.man = cValue;
}
else if (methodName == #"models")
{
self.mod = cValue;
}
else if (methodName == #"subMod")
{
self.sub = cValue;
}
This is not the correct way to compare strings (you are actually comparing the addresses of the strings so they will probably never be the same), instead you should use this:
if ([methodName isEqualToString:#"manufacturers"])
{
self.man = cValue;
}
else if ([methodName isEqualToString:#"models"])
{
self.mod = cValue;
}
else if ([methodName isEqualToString:#"subMod"])
{
self.sub = cValue;
}
Another issue is that in readPlistData you are reading the keys "Man", "Mod", and "SubMod" but they should be the same as what you write into the dictionary: "Manufacturers", "Models", and "SubModels". (Either one could be correct, but they need to match so that you are reading and writing the same keys!)
Lastly, are you using the same instance of EngineProperties each time that you call saveData or are you creating a new object each time? If you aren't using the same object, then the iVars will be reset to nil each time that you call it.
First of all when you are calling
- (void) saveData:(NSString *)methodName signature:(NSString *)pSignature Version:(NSNumber *)pVersion request:(NSNumber *)rNumber dataVersion:(NSNumber *)dvReturned cacheValue:(NSNumber *)cValue;
Refer this to compare the string value
//This opens up and reads the current plist ready to be used
-(void) readPlistData
{
// Data.plist code
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
// check to see if Data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// if not in documents, get property list from main bundle
plistPath = [[NSBundle mainBundle] pathForResource:#"EngineProperties" ofType:#"plist"];
}
// read property list into memory as an NSData object
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSPropertyListFormat format;
// convert static property liost into dictionary object
NSDictionary *tempRoot = (NSMutableDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
man = [cacheValue objectForKey:#"Man"];
mod = [cacheValue objectForKey:#"Mod"];
sub = [cacheValue objectForKey:#"SubMod"];
if (tempRoot && [tempRoot count]){
// assign values
self.signature = [tempRoot objectForKey:#"Signature"];
self.version = [tempRoot objectForKey:#"Version"];
self.request = [tempRoot objectForKey:#"Request"];
self.dataVersion = [tempRoot objectForKey:#"Data Version"];
cacheValue = [tempRoot objectForKey:#"Cache Value"];
}
}
- (void) saveData:(NSString *)methodName signature:(NSString *)pSignature Version:(NSNumber *)pVersion request:(NSNumber *)rNumber dataVersion:(NSNumber *)dvReturned cacheValue:(NSNumber *)cValue
{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
// set the variables to the values in the text fields
self.signature = pSignature;
self.version = pVersion;
self.request = rNumber;
self.dataVersion = dvReturned;
//do some if statment stuff here to put the cache in the right place or what have you.
if (methodName isEqualToString:#"manufacturers") {
self.man = cValue;
} else if (methodName isEqualToString:#"models") {
self.mod = cValue;
} else if (methodName isEqualToString:#"subMod") {
self.sub = cValue;
}
self.cacheValue = [NSDictionary dictionaryWithObjectsAndKeys:
man, #"Manufacturers",
mod, #"Models",
sub, #"SubModels", nil];
NSDictionary *plistDict = [NSDictionary dictionaryWithObjectsAndKeys:
signature, #"Signature",
version, #"Version",
request, #"Request",
dataVersion, #"Data Version",
cacheValue, #"Cache Value", nil];
NSString *error = nil;
// create NSData from dictionary
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
// check is plistData exists
if(plistData) {
// write plistData to our Data.plist file
[plistData writeToFile:plistPath atomically:YES];
NSString *myString = [[NSString alloc] initWithData:plistData encoding:NSUTF8StringEncoding];
// NSLog(#"%#", myString);
} else {
NSLog(#"Error in saveData: %#", error);
// [error release];
}
}
#end

iPhone - properties of a list of files

I have an mutableArray which displays a list of files in a tableView. I have stored those files in a directory.
I am using NSfileManager class and attributesOfItemAtPath method to retrieve some properties of the files like fileSize, fileType..
In the second stage I am trying to have a detailView controller which displays the properties of the particular file when touched in the tableView.
The problem is I dont know how to bind those properties like fileSize and fileType separately to a NSDictionary and make it display in the detailView for that particular file.
Here is my source code for listing the files and to list the properties.
- (void)listFiles {
NSFileManager *fm =[NSFileManager defaultManager];
NSError *error = nil;
NSString *parentDirectory = #"/Users/akilan/Documents";
NSArray *paths = [fm contentsOfDirectoryAtPath:parentDirectory error:&error];
if (error) {
NSLog(#"%#", [error localizedDescription]);
error = nil;
}
directoryContent = [[NSMutableArray alloc] init];
for (NSString *path in paths){
NSString *documentsDirectory = [[path lastPathComponent] stringByDeletingPathExtension];
[directoryContent addObject:documentsDirectory];
}
}
-(void) willProcessPath {
NSString *parentDirectory = #"/Users/akilan/Documents";
NSArray *paths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:parentDirectory error:nil];
for (NSString *path in paths){
filesPath = [parentDirectory stringByAppendingPathComponent:path];
NSDictionary *attrDir = [[NSFileManager defaultManager]attributesOfItemAtPath:filesPath error:nil];
filesSize = [attrDir objectForKey:NSFileSize];
filesName = (NSString *)directoryContent;
filesType = [path pathExtension];
createdDate = [attrDir objectForKey:NSFileCreationDate];
modifiedDate = [attrDir objectForKey:NSFileModificationDate];
}
}
Please help me to proceed to the next step.. Thanks in advance..
Try something like this:
- (void)viewDidLoad {
[super viewDidLoad];
[self listFiles];
}
- (void)listFiles {
NSFileManager *fm =[NSFileManager defaultManager];
NSError *error = nil;
NSString *parentDirectory = [self getDocumentsPath];
NSArray *paths = [fm contentsOfDirectoryAtPath:parentDirectory error:&error];
if (error) {
NSLog(#"%#", [error localizedDescription]);
error = nil;
}
NSMutableArray *array = [[NSMutableArray alloc] init];
self.directoryContent = array;
[array release];
for (NSString *path in paths){
NSString *fullPath = [self getPathToFileInDocumentsDirectory:path];
NSMutableDictionary *filePropertiesDictionary = [NSMutableDictionary dictionaryWithDictionary:[[NSFileManager defaultManager] attributesOfItemAtPath:fullPath error:nil]];
[filePropertiesDictionary setObject:fullPath forKey:#"fullPath"];
[filePropertiesDictionary setObject:path forKey:#"fileName"];
[directoryContent addObject:filePropertiesDictionary];
}
NSLog(#"%#", directoryContent);
}
- (NSString *)getDocumentsPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryPath = [paths objectAtIndex:0];
return documentsDirectoryPath;
}
- (NSString *)getPathToFileInDocumentsDirectory:(NSString *)fileName {
return [[self getDocumentsPath] stringByAppendingPathComponent:fileName];
}
Why not create a class "FileDescriptor" with the following properties:
NSString *fileName;
NSString *fileSize;
NSString *fileType;
NSDate *createdDate;
NSDate *modifiedDate;
When iterating over your directory, you create an instance of this class for each file, and store that in an array (or a dictionary, if you like). Then you can use that array to
a) provide data for your UITableViewCells
b) show a detail view for each file when the cell is clicked.
Should you want to use a dictionary instead (which I think is a lot less "clean") you can do so like this:
NSDictionary *fileProperties = [NSDictionary dictionaryWithObjectsAndKeys:
fileSize, #"fileSize",
fileName, #"fileName", nil];
- (void)listFiles {
NSFileManager *fm =[NSFileManager defaultManager];
NSError *error = nil;
NSString *parentDirectory = #"/Users/akilan/Documents";
NSArray *paths = [fm contentsOfDirectoryAtPath:parentDirectory error:&error];
if (error) {
NSLog(#"%#", [error localizedDescription]);
error = nil;
}
directoryContent = [[NSMutableArray alloc] init];
for (NSString *path in paths){
NSString *documentsDirectory = [[path lastPathComponent] stringByDeletingPathExtension];
[directoryContent addObject:documentsDirectory];
}
}
-(void) willProcessPath {
NSString *parentDirectory = #"/Users/akilan/Documents";
NSArray *paths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:parentDirectory error:nil];
for (NSString *path in paths){
filesPath = [parentDirectory stringByAppendingPathComponent:path];
NSDictionary *attrDir = [[NSFileManager defaultManager]attributesOfItemAtPath:filesPath error:nil];
filesDirectory = [NSDictionary dictionaryWithObjectsAndKeys:filesSize, [attrDir objectForKey:NSFileSize],
filesName, (NSString *)directoryContent, filesType, [path pathExtension],
createdDate, [attrDir objectForKey:NSFileCreationDate],
modifiedDate, [attrDir objectForKey:NSFileModificationDate], nil];
NSLog(#"%#", filesDirectory);
}
}
#import <UIKit/UIKit.h>
#interface MyFilesList : UIViewController <UITableViewDelegate, UITableViewDataSource> {
NSMutableArray *directoryContent;
IBOutlet UITableView *filesTable;
IBOutlet NSNumber *filesSize;
IBOutlet NSString *filesName;
IBOutlet NSString *filesType;
IBOutlet NSString *filesPath;
IBOutlet NSDate *createdDate;
IBOutlet NSDate *modifiedDate;
NSMutableDictionary *filesDirectory;
}
#property (nonatomic, retain) NSMutableArray *directoryContent;
#property (nonatomic, retain) IBOutlet UITableView *filesTable;
#property (nonatomic, retain) IBOutlet NSNumber *filesSize;
#property (nonatomic, retain) IBOutlet NSString *filesName;
#property (nonatomic, retain) IBOutlet NSString *filesType;
#property (nonatomic, retain) IBOutlet NSString *filesPath;
#property (nonatomic, retain) IBOutlet NSDate *createdDate;
#property (nonatomic, retain) IBOutlet NSDate *modifiedDate;
#property (nonatomic, retain) NSMutableDictionary *filesDirectory;
- (void)listFiles;
- (void) willProcessPath;
#end