NSDictionary value replacing instead of adding in Plist - objective-c

hi i tried to add values(book id,page number,notes) from NSdictionary to Plist but each time the new value replacing the previous one?but i need all values in plist my code for adding dictionary to plist is
NSString *bid=#"95";
NSString *pnum=#"12";
userNotes=[[NSMutableDictionary alloc]init];
[userNotes setValue:userNotesTextview.text forKey:#"notes"];
[userNotes setValue:bid forKey:#"bookid"];
[userNotes setValue:pnum forKey:#"pagenumber"];
userNotesView.hidden=YES;
_background.hidden = YES;
userNotesTextview.text=#"";
[self savingMetaData];
NSMutableArray *notes=[[NSMutableArray alloc]init];
[notes addObject:userNotes];
NSMutableDictionary *final=[[NSMutableDictionary alloc]init];
[final setValue:notes forKey:#"usernotes"];
[final writeToFile:metaDataPath atomically:YES];
and my plist look like
how can i solve this problem

Fetch the existing array from the plist as below, but first make sure you have copied you plist to Documents directory or, to some writable folder as below
NSFileManager *fileManager=[NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *docPath=[[paths objectAtIndex:0] stringByAppendingString:#"yourplist.plist"];
BOOL fileExists = [fileManager fileExistsAtPath: docPath];
NSError *error = nil;
if(!fileExists)
{
NSString *strSourcePath = [[NSBundle mainBundle] pathForResource:#"yourplist" ofType:#"plist"];
[fileManager copyItemAtPath:strSourcePath toPath:docPath error:&error];
}
NSString *path = docPath;
NSMutableDictionary *plistdictionary = [[NSMutableDictionary alloc]initWithContentsOfFile:path];
NSMutableArray *notes=[plistdictionary objectForKey:#"usernotes"];
if(notes==nil){
notes=[[NSMutableArray alloc] init];
}
NSString *bid=#"95";
NSString *pnum=#"12";
userNotes=[[NSMutableDictionary alloc]init];
[userNotes setValue:userNotesTextview.text forKey:#"notes"];
[userNotes setValue:bid forKey:#"bookid"];
[userNotes setValue:pnum forKey:#"pagenumber"];
[notes addObject:userNotes];
then finally
NSMutableDictionary *final=[[NSMutableDictionary alloc]init];
[final setValue:notes forKey:#"usernotes"];
[final writeToFile:docPath atomically:YES];
Note: You cannot write anything in MainBundle, so better to copy your plist to Documents directory and use from there..

because plist can store value with unique key only. if you try to save value with same key it will replace old one with new value. so always save new value with new key (eg. item0, item1, item3 etc.)
following line will store two usernote with key #"usernotes1" and #"usernotes2" respectively
[final setValue:notes forKey:#"usernotes1"];
[final setValue:notes forKey:#"usernotes2"];

Plist structure looks like this
You can create a UserNote model class.
#define kBookID #"bookid"
#define kPageNumber #"pageNumber"
#define kNotes #"notes"
#interface UserNote : NSObject
#property (nonatomic, copy) NSString *bookID;
#property (nonatomic, copy) NSString *pageNumber;
#property (nonatomic, copy) NSString *notes;
- (id)initWithDictionary:(NSDictionary *)dictionary;
+ (NSArray *)savedUserNotes;
- (void)save;
#end
Initialize
- (id)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self)
{
self.bookID = dictionary[kBookID];
self.pageNumber = dictionary[kPageNumber];
self.notes = dictionary[kNotes];
}
return self;
}
Find the document path of plist file in documents directory. If the plist file is not there create a new one and return the path.
+ (NSString *)userNotesDocumentPath
{
NSString *documentsPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#"UserNotes.plist"];
NSFileManager *fileManger = [NSFileManager defaultManager];
if (![fileManger fileExistsAtPath:documentsPath])
{
NSString *bundleResourcePath = [[NSBundle mainBundle]pathForResource:#"UserNotes" ofType:#"plist"];
NSArray *userNotes = [NSArray arrayWithContentsOfFile:bundleResourcePath];
[userNotes writeToFile:documentsPath atomically:YES];
}
return documentsPath;
}
Fetches all saved usernotes from plist file.
+ (NSArray *)savedUserNotes
{
NSString *documentsPath = [self userNotesDocumentPath];
NSArray *savedNotes = [NSArray arrayWithContentsOfFile:documentsPath];
NSMutableArray *savedUserNotes = [#[] mutableCopy];
for (NSDictionary *dict in savedNotes) {
UserNote *note = [[UserNote alloc]initWithDictionary:dict];
[savedUserNotes addObject:note];
}
return savedUserNotes;
}
Saves a usenote to plist
- (NSDictionary *)userNoteDictionary
{
return #{kBookID:self.bookID,kPageNumber:self.pageNumber,kNotes:self.notes};
}
- (void)saveUserNotesToPlist:(NSArray *)userNotes
{
NSMutableArray *mutableUserNotes = [#[] mutableCopy];
for (UserNote *note in userNotes) {
NSDictionary *dict = [note userNoteDictionary];
[mutableUserNotes addObject:dict];
}
NSString *documentsPath = [UserNote userNotesDocumentPath];
[mutableUserNotes writeToFile:documentsPath atomically:YES];
}
#pragma mark - Save
- (void)save
{
NSMutableArray *savedNotes = [[UserNote savedUserNotes] mutableCopy];
[savedNotes addObject:self];
[self saveUserNotesToPlist:savedNotes];
}
In you viewController where user makes a note
- (IBAction)saveUserNoteButtonPressed:(UIButton *)button
{
UserNote *note = [UserNote new];
note.bookID = #"95";
note.pageNumber = #"12";
note.notes = self.userNotesTextview.text;
[note save];
}
Demo Source Code

Related

Exporting all contacts in one .vcf file using Contacts.Framework in Objective - C

Using I AddressBook.framework I used to create Contacts.vcf from all contacts and save it in Documents Directory.
Here is the code I used to use :
ABAddressBookRef addressBook1 = ABAddressBookCreate();
NSArray *arrayOfAllPeople = (__bridge_transfer NSArray *) ABAddressBookCopyArrayOfAllPeople(addressBook1);
long cnt = (unsigned long)[arrayOfAllPeople count];
if (cnt==0) {
ABAddressBookRequestAccessWithCompletion(addressBook1, nil);
}
if(ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized)
{
ABAddressBookRef addressBook2 = ABAddressBookCreate();
CFArrayRef contacts = ABAddressBookCopyArrayOfAllPeople(addressBook2);
CFDataRef vcards = (CFDataRef)ABPersonCreateVCardRepresentationWithPeople(contacts);
NSString *vcardString = [[NSString alloc] initWithData:(__bridge NSData *)vcards encoding:NSUTF8StringEncoding];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *folderPath = [paths objectAtIndex:0];
NSString *filePath = [folderPath stringByAppendingPathComponent:#"Contacts.vcf"];
[vcardString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
CFRelease(addressBook2); }
How do I create a Contacts.vcf file having all device contacts using Contacts.framework and save it in documents directory ?
You can use this method to get all the contacts in .vcf file. It return the same output that you get using AddressBook.framework.
- (void)getContacts {
NSMutableArray *contactsArray=[[NSMutableArray alloc] init];
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!granted) {
dispatch_async(dispatch_get_main_queue(), ^{
});
return;
}
NSMutableArray *contacts = [NSMutableArray array];
NSError *fetchError;
CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:#[[CNContactVCardSerialization descriptorForRequiredKeys], [CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName]]];
BOOL success = [store enumerateContactsWithFetchRequest:request error:&fetchError usingBlock:^(CNContact *contact, BOOL *stop) {
[contacts addObject:contact];
}];
if (!success) {
NSLog(#"error = %#", fetchError);
}
// you can now do something with the list of contacts, for example, to show the names
CNContactFormatter *formatter = [[CNContactFormatter alloc] init];
for (CNContact *contact in contacts) {
[contactsArray addObject:contact];
// NSString *string = [formatter stringFromContact:contact];
//NSLog(#"contact = %#", string);
}
//NSError *error;
NSData *vcardString =[CNContactVCardSerialization dataWithContacts:contactsArray error:&error];
NSString* vcardStr = [[NSString alloc] initWithData:vcardString encoding:NSUTF8StringEncoding];
NSLog(#"vcardStr = %#",vcardStr);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *folderPath = [paths objectAtIndex:0];
NSString *filePath = [folderPath stringByAppendingPathComponent:#"Contacts.vcf"];
[vcardStr writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}];
}
From iOS 9+ version AddressBookUI.framework and Addressbook.framework becomes deprecated. Apple introduced ContactUI.framework and Contact.framework with enhancements over AddressBookUI.framework and Addressbook.framework. In this blog we will talk about how to use these two new frameworks and export VCard. Let’s start picking contact from phone contacts and access basic information of that person.
Step 1. Create new Xcode project name ContactDemo and import Contacts.framework and ContactsUI.framework as shown in picture.
Step 2. In project add UIButton, UIImageView and 3 UILabels as shown in picture :
Step 3. Create outlets of button action, imageview and labels in respective view controller as :
#property (weak, nonatomic) IBOutlet UIImageView *personImage;
#property (weak, nonatomic) IBOutlet UILabel *personName;
#property (weak, nonatomic) IBOutlet UILabel *emailId;
#property (weak, nonatomic) IBOutlet UILabel *phoneNo;
- (IBAction)selectAction:(id)sender;
Step 4. Add delegate CNContactPickerDelegate to viewController.
Step 5. Add delegate method :
- (void) contactPicker:(CNContactPickerViewController *)picker
didSelectContact:(CNContact *)contact {
[self getContactDetails:contact];
}
This delegate method will return contact in the form of CNContact object which will be further processed in local method
-(void)getContactDetails:(CNContact *)contactObject {
NSLog(#"NAME PREFIX :: %#",contactObject.namePrefix);
NSLog(#"NAME SUFFIX :: %#",contactObject.nameSuffix);
NSLog(#"FAMILY NAME :: %#",contactObject.familyName);
NSLog(#"GIVEN NAME :: %#",contactObject.givenName);
NSLog(#"MIDDLE NAME :: %#",contactObject.middleName);
NSString * fullName = [NSString stringWithFormat:#"%# %#",contactObject.givenName,contactObject.familyName];
[self.personName setText:fullName];
if(contactObject.imageData) {
NSData * imageData = (NSData *)contactObject.imageData;
UIImage * contactImage = [[UIImage alloc] initWithData:imageData];
[self.personImage setImage:contactImage];
}
NSString * phone = #"";
NSString * userPHONE_NO = #"";
for(CNLabeledValue * phonelabel in contactObject.phoneNumbers) {
CNPhoneNumber * phoneNo = phonelabel.value;
phone = [phoneNo stringValue];
if (phone) {
userPHONE_NO = phone;
}}
NSString * email = #"";
NSString * userEMAIL_ID = #"";
for(CNLabeledValue * emaillabel in contactObject.emailAddresses) {
email = emaillabel.value;
if (email) {
userEMAIL_ID = email;
}}
NSLog(#"PHONE NO :: %#",userPHONE_NO);
NSLog(#"EMAIL ID :: %#",userEMAIL_ID);
[self.emailId setText:userEMAIL_ID];
[self.phoneNo setText:userPHONE_NO];
}
Step 6. Create CNContactPickerViewController class object and register its delegate in button IBAction method :
- (IBAction) selectAction:(id)sender {
CNContactPickerViewController *contactPicker = [CNContactPickerViewController new];
contactPicker.delegate = self;
[self presentViewController:contactPicker animated:YES completion:nil];
}
[self presentViewController:contactPicker animated:YES completion:nil]; will present view of contact list.
Step 7. Run project
A . Main View
B. On Tapping “Select Contact” button CNContactPickerViewController will open as shown in picture :
C. Pick one contact and view will dismiss and you will get details of that contact as shown in picture :
Earlier we have write permission code to access contacts but now it implicitly grants permission for accessing contacts. With this framework we can also generate VCard(VCF) and share among other platforms. Here is the steps to create VCard.
Step 1. Pick contact from CNContactPickerViewController and you will get CNContact Object in delegate as mention above.
Step 2. Save contact in document directory. As data is stored in NSData form so to convert contact to NSData
use CNContactVCardSerialization class that represents VCard in NSData format.
- (NSString *) saveContactToDocumentDirectory:(CNContact *)contact {
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString * VCardPath = [documentsDirectory stringByAppendingString:#"/VCard.vcf"];
NSArray *array = [[NSArray alloc] initWithObjects:contact, nil];
NSError *error;
NSData *data = [CNContactVCardSerialization dataWithContacts:array error:&error];
[data writeToFile:VCardPath atomically:YES];
return VCardPath;
}
CNContactVCardSerialization class method dataWithContacts:error: takes array of contact objects(CNContact class Object).
saveContactToDocumentDirectory method will return the file path of Vcard. With File path you can export contact anywhere you want.
Source: Contacts UI, Contacts Framework and create VCard(VCF) in Objective-C

How to manage data in a iOS app

I'm developing an app where I have some data. It would be some strings I want to use.
The issue is I would like to initialize with the data of a file. something like:
#property (strong, nonatomic) Cow *cow;
_cow = [[Cow alloc] init (NSString *) #"Some string"];
I have the proper constructor for cow that accepts an string .
What I want is the way I can initialize severals cows with different strings and repeat some values if I want.
I was thinking in a text file where I have all the strings, and just pick those I want.
Another possibility is a db like SQlite.
Reading your comments to other answers, it seems that you are interested in an object graph persistence mechanism for your application. The answer given by #Greg - object graph serialization - is excellent. In the default case, you'd be reading your entire object graph into memory, which may or may not be what you want. (There are ways of dealing with that too.) You asked about how to choose data you want. In the case where your Cow's are in memory, you can find your Cow of choice in a lot of ways. For example:
#import <Foundation/Foundation.h>
#interface Cow : NSObject
#property (nonatomic,copy) NSString *color;
#end
#implementation Cow
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Cow *brownCow = [Cow new];
brownCow.color = #"brown";
Cow *whiteCow = [Cow new];
whiteCow.color = #"white";
NSArray *cows = #[brownCow,whiteCow];
NSUInteger cowIndex = [cows indexOfObjectPassingTest:^BOOL(Cow *obj, NSUInteger idx, BOOL *stop) {
return [[obj color] isEqualToString:#"brown"];
}];
if( cowIndex != NSNotFound )
NSLog(#"Your brown cow is: %#",cows[cowIndex]);
else
NSLog(#"No brown cow");
}
}
But there are other object persistence frameworks that you may consider. For example, Core Data is commonly used. sqlite3 is another possibility, which when used with an object-oriented abstraction layer like FMDB is another good choice.
The answer boils down to your requirements. How many Cow objects might there be? What are the performance requirements? And so on.
You can make your object Cow conform to a NSCoding protocol:
//Cow.h file
#interface Cow : NSObject <NSCoding>
#property(nonatomic, copy) NSString *someString;
#end
//Cow.m
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
_someString = [aDecoder decodeObjectForKey:#"someString"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.someString forKey:#"someString"];
}
And after that you can save it to a file (you can create array of Cow objects and save it to a file):
NSrray *data = #[cow1, cow2, cow3];
[NSKeyedArchiver archiveRootObject:data toFile:[self pathToFile]];
And you can read it like that:
NSFileManager* fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:[self pathToData]])
{
NSData *data = [NSData dataWithContentsOfFile:[self pathToData]];
NSMutableArray *dataArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// dataArray -> array with your data
}
Hopefully I understood your question.
//EDITED
Get file to path:
-(NSString*)pathToData
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"MyData"];
NSFileManager* fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:filePath])
{
[fileManager createDirectoryAtPath:filePath withIntermediateDirectories:NO attributes:nil error:nil];
}
return [filePath stringByAppendingPathComponent:#"mydata.plist"];
}
Save and remove data:
-(BOOL)save
{
NSMutableArray *data = [self cachedDataFromFile];
if (!data)
{
NSLog(#"data = nil");
data = [NSMutableArray arrayWithArray:#[self]];
}
else
[data addObject:self];
return [NSKeyedArchiver archiveRootObject:data toFile:[self pathToData]];
}
-(void)removeFromFile
{
NSMutableArray *data = [self cachedDataFromFile];
if (!data)
return;
else
{
[data removeObject:self];
[NSKeyedArchiver archiveRootObject:data toFile:[self pathToData]];
}
}
-(NSMutableArray*)cachedDataFromFile
{
NSFileManager* fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:[self pathToData]])
{
NSData *data = [NSData dataWithContentsOfFile:[self pathToData]];
NSMutableArray *dataArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return dataArray;
}
return nil;
}
Let me know is it work for you.

Inputstring is null after being set in a static method, why?

I am uncertain of how memory is managed in my particular case...
I have two methods:
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath
{
NSString * destPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
destPath = [destPath stringByAppendingPathComponent:
[NSString stringWithFormat:#"%#.plist", name]];
if (![[NSFileManager defaultManager] fileExistsAtPath:destPath])
{
[[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] pathForResource:name ofType:#"plist"] toPath:destPath error:nil];
}
plistPath = [NSMutableString stringWithString:[destPath copy]];
NSData * plistXML =
[[NSFileManager defaultManager] contentsAtPath:plistPath];
NSLog(#"AFTER plistPath: \n%#",plistPath);
return
(NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:format
errorDescription:&errorDesc];
}
+(bool)writeToCache:(NSString*) data andField: (NSString*) field
{
NSString * errorDesc = nil;
NSPropertyListFormat format;
NSMutableString * plistPath;
NSMutableDictionary * temp = [BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:plistPath];
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
return false;
}
NSMutableArray * arr = [temp objectForKey:field];
[arr addObject:data];
NSLog(#"path: %#",plistPath);
// Write to plist
bool res = [temp writeToFile:plistPath atomically:YES];
NSLog(#"RES: %d", res);
return true;
}
The problem is that the bottom method that sends in "plistPath" to the above method retrives a null plistPath after the above method has set it. Why and how can I fix this?
NSLog(#"path: %#",plistPath);
in the bottom method shows null, why?
I use ARC. Also "destPath" is set and shows the correct path.
I believe you could be a bit confused here.
You are creating plistPath in the bottom method. And then you pass plistPath into
[BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:plistPath];
but plistPath is NULL
NSMutableString * plistPath; // Is NULL
But once it has been passed in the local plistPath takes over.
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath // Notice the local plistPath variable. This is the one you are playing with in this method.
At this point you are setting plistPath but remember it is still just a local variable and not an instance variable. So the button method will never know about it being set, as far as the button method is concerned it is still NULL
plistPath = [NSMutableString stringWithString:[destPath copy]];
So whatever you set in plistPath in the top method will not get passed back to the bottom method, think of the top plistPath as being deallocated when the method does the return.
So the plistPath in the bottom method will remain NULL
So try this instead SOLUTION
static NSMutableString *yourNewStringforPlistPath; //This will be NULL
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath
{
NSString * destPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
destPath = [destPath stringByAppendingPathComponent:
[NSString stringWithFormat:#"%#.plist", name]];
if (![[NSFileManager defaultManager] fileExistsAtPath:destPath])
{
[[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] pathForResource:name ofType:#"plist"] toPath:destPath error:nil];
}
yourNewStringforPlistPath = [NSMutableString stringWithString:[destPath copy]];
NSData * plistXML =
[[NSFileManager defaultManager] contentsAtPath:yourNewStringforPlistPath];
NSLog(#"AFTER plistPath: \n%#",yourNewStringforPlistPath);
return
(NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:format
errorDescription:&errorDesc];
}
+(bool)writeToCache:(NSString*) data andField: (NSString*) field
{
NSString * errorDesc = nil;
NSPropertyListFormat format;
NSMutableDictionary * temp = [BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:[NSNull null]]; // As this is already NULL you don't really need to pass yourNewStringforPlistPath in unless in the future this value can be set before this.
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
return false;
}
NSMutableArray * arr = [temp objectForKey:field];
[arr addObject:data];
NSLog(#"path: %#",yourNewStringforPlistPath);
// Write to plist
bool res = [temp writeToFile:yourNewStringforPlistPath atomically:YES];
NSLog(#"RES: %d", res);
return true;
}

A function to write data to plist in Objective-c

Suppose I have a custom function called savePlist as below.
CommonClass.m
- (NSString *)getDirectoryPath
{
NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [pathList objectAtIndex:0];
return path;
}
- (void)savePlist:(NSString *)fileName WithArray:(NSArray *)fileArr
{
NSString *path = [[self getDirectoryPath] stringByAppendingPathComponent:fileName];
[fileArr writeToFile:path atomically:YES];
}
While I run a code as below, no plist is generated.
ABCAppDelegate.m
...
[cc savePlist:#"example.plist" WithArray:[NSArray new]];
The file of example.plist has not been generated, is there any mistakes on my code?
Thanks
UPDATE:
If I use the code as below, the xxx.plist file has been generated successfully.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *plistFile = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"abc.plist"];
NSMutableDictionary *plist = [[NSMutableDictionary alloc] initWithContentsOfFile:plistFile];
if (!plist) {
plist = [NSMutableDictionary new];
[plist writeToFile:plistFile atomically:YES];
NSLog(#"write plist");
}
Reference: link
UPDATE 2:
I change the code as below:
NSLog(#"code");
[cc savePlist:#"example.plist" WithArray:[NSArray new]];
NSLog(#"code");
- (void)savePlist:(NSString *)fileName WithArray:(NSArray *)fileArr
{
NSLog(#"fileName = %#, fileArr = %#", fileName, fileArr);
NSString *path = [[self getDirectoryPath] stringByAppendingPathComponent:fileName];
NSLog(#"path = %#", path);
[fileArr writeToFile:path atomically:YES];
}
It just print out:
2011-10-10 14:24:00.560 ABC[3390:207] code
2011-10-10 14:24:00.561 ABC[3390:207] code
No message of fileName = and fileArr =, also path = print out on the log, so the code inside savePlist have not been executed?
It looks like your common class object is not instantiated, and you are sending all those messages to nil. Try logging cc in your app delegate code, where you call this method from.
You need to create an instance of your common class, something like
cc = [[CommonClass alloc] init];
(may vary depending on your set up).

Saving data with NSMutableDictionary

I had a method to save a dic to the disk:
+(BOOL) writeApplicationData:(NSDictionary *)data
bwriteFileName:(NSString *)fileName
{
NSLog(#"writeApplicationData");
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
if (!documentsDirectory) {
NSLog(#"Documents directory not found!");
return NO;
}
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
return ([data writeToFile:appFile atomically:YES]);
}
And I tested it with:
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
NSMutableDictionary *d1 = [[NSMutableDictionary alloc] init];
NSMutableDictionary *d2 = [[NSMutableDictionary alloc] init];
[d1 setObject:#"d11"
forKey:#"d11"];
[d1 setObject:#"d12"
forKey:#"d12"];
[d1 setObject:#"d13"
forKey:#"d13"];
[d2 setObject:#"d21"
forKey:#"d21"];
[d2 setObject:#"d22"
forKey:#"d22"];
[d2 setObject:#"d23"
forKey:#"d23"];
[dic setObject:d1
forKey:#"d1"];
[dic setObject:d2
forKey:#"d2"];
[self writeApplicationData:dic
bwriteFileName:#"testSave"];
And the data is saved correctly.
Then I tried to save d1 with class obj in it:
LevelInfoData *levelInfoData = [[LevelInfoData alloc] init];
[levelInfoDictionary setObject:levelInfoData
forKey:#"test"];
[dic setObject:levelInfoDictionary
forKey:#"LevelInfoDictionary"];
But this time, even no plist file was generated in the disk.
Here is the LevelInfoData class:
#interface LevelInfoData : NSObject {
int levelNum;
}
#property (nonatomic) int levelNum;
#end
#implementation LevelInfoData
#synthesize levelNum;
#synthesize isLevelLocked;
#synthesize isLevelCleared;
#synthesize levelHighScore;
-(id)init
{
if( (self = [super init]) ) {
levelNum = 0;
}
return self;
}
#end
I'm really confused, hope somebody could help me out, thanks.
The contents of the dictionary need to be property list type objects.
From the NSDictionary Class Reference:
This method recursively validates that all the contained objects are property list objects (instances of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary) before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.
https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html
You may want to try making your custom class a subclass of NSData rather than NSObject.
I'm not sure how attached you are to NSDictionary, but this may be a situation where NSCoder will better serve you.
See nscoder vs nsdictionary when do you use what
More details here:
NSCoder Class Reference
Some code snippets
A tutorial