Issue with writing XMLDocument - objective-c

I have a non doc-based app with table view with two columns, whose Value bindings are bound to two properties of the arrangedObjects of an array controller. I have a NSMutableArray in my AppDelegate that hold the data, as well as the corresponding #property and #synthesize. I implement key-value-coding accessor methods and use those when inserting data to the array in order to generate KVO notifications. I fill up my array with some NSDictionary objects from xml file. I've got all that working (modify, remove and add items), but what I'd like to do is write it back to xml file.
xmlData writeToFile:fileName atomically:YES
is writing to disk, but don't reflect changes when editing table view. There must be some tiny little thing I am forgetting. What is it?
Any suggestions would be very nice!
The code:
// AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>{
NSXMLDocument *xmlDoc;
NSXMLElement *root;
NSMutableArray *children;
}
#property (assign) IBOutlet NSWindow *window;
#property (nonatomic, copy)NSArray *children;
- (void)setChildren:(NSArray *)newChildren;
- (NSUInteger)countOfChildren;
- (id)objectInChildrenAtIndex:(NSUInteger)index;
- (void)insertObject:(id)object inChildrenAtIndex:(NSUInteger)index;
- (void)removeObjectFromChildrenAtIndex:(NSUInteger)index;
- (IBAction)addObject:(id)sender;
- (IBAction)saveXMLToFile:(id)sender;
- (void) populateTable;
#end
// AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
-(void) addObjectToChildren:(id)object{
[self insertObject:object inChildrenAtIndex:[self countOfChildren]];
}
#synthesize children;
- (id)init{
self = [super init];
if (self) {
children = [[NSMutableArray alloc] init];
}
return self;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
[self populateTable];
}
#pragma mark - Indexed Accessors
- (void)setChildren:(NSArray *)newChildren{
if (children!=newChildren) {
children=[newChildren mutableCopy];
}
}
-(NSUInteger)countOfChildren{
return [self.children count];
}
-(id)objectInChildrenAtIndex:(NSUInteger)index{
return [self.children objectAtIndex:index];
}
- (void)insertObject:(id)object inChildrenAtIndex:(NSUInteger)index{
[children insertObject:object atIndex:index];
}
-(void)removeObjectFromChildrenAtIndex:(NSUInteger)index{
[children removeObjectAtIndex:index];
}
#pragma mark -
-(void)populateTable{
NSError *error=nil;
NSString *pathname = [#"~/myFile.xml" stringByExpandingTildeInPath];
NSURL *url = [NSURL fileURLWithPath:pathname];
xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyXML error:&error];
root = [xmlDoc rootElement];
NSArray *array=[root nodesForXPath:#"number" error:&error];
NSMutableArray *mArray=[[NSMutableArray alloc]init];
for (NSXMLElement *element in array) {
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:[element stringValue],#"id",#"number", #"name", nil];
[mArray addObject:dict];
}
[self setChildren:[NSArray arrayWithArray:mArray]];
}
#pragma mark - Actions
- (IBAction)addObject:(id)sender {
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:#"0",#"id",#"number", #"name", nil];
NSXMLElement *element=[[NSXMLElement alloc]initWithName:[dict objectForKey:#"name"] stringValue:[dict objectForKey:#"id"]];
[root insertChild:element atIndex:[self countOfChildren]];
[self addObjectToChildren:dict];
}
- (IBAction)saveXMLToFile:(id)sender {
NSString *fileName = [#"~/myFile.xml" stringByExpandingTildeInPath];
NSData *xmlData = [xmlDoc XMLDataWithOptions:NSXMLNodePrettyPrint];
if (![xmlData writeToFile:fileName atomically:YES]) {
NSBeep();
NSLog(#"Could not write document out...");
}
}
#end
The xml file:
<root>
<number>0</number>
<number>1</number>
<number>2</number>
<number>3</number>
<number>4</number>
<number>5</number>
</root>

When you change values in the tableview the underlying array is updated but not your XML document. You need to update your XML document or recreate it from the array to hold edited values.

Related

Empty UITableViewController with async calls

I am working on using NSURLSession and JSON serialization to fetch content from my site. The async calls and getting the JSON data work perfectly. My issue is, when it comes to displaying the data in the TableviewController, I put an NSLog statement to see if there is data and there is, but that cell.textlable.text never updates. I'm guessing the issue is the threads but I can't figure it out. Can you help?
#interface MainTableViewController :
UITableViewController<LokalModelProtocol>
#property (strong,nonatomic) NSMutableArray* arr;
#end
#implementation MainTableViewController
#synthesize arr;
- (void)viewDidLoad {
[super viewDidLoad];
arr = [[NSMutableArray alloc]init];
LokalModel *lokal = [[LokalModel alloc]init];
lokal.delegate=self;
[lokal downloadItems];
}
-(void)itemsDownloaded:(NSMutableArray *)items
{
arr=items;
//NSLog(#"%#", items);
[self.tableView reloadData];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
#warning Incomplete implementation, return the number of sections
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section {
#warning Incomplete implementation, return the number of rows
// return 1;
return [arr count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"mainCell" forIndexPath:indexPath];
PostModel *post = [[PostModel alloc]init];
post =[arr objectAtIndex:indexPath.row];
NSLog(#"%#", post.postTitle); ////this outputs the correct strings///////
cell.textLabel.text =[NSString stringWithFormat:#"%#", post.postTitle];
cell.detailTextLabel.text = post.postTitle;///neither of these do//////
return cell;
}
#end
#protocol LokalModelProtocol <NSObject,NSURLSessionDelegate>
+(void)itemsDownloaded:(NSMutableArray*)items;
#end
#interface LokalModel : NSObject
-(void)downloadItems;
#property (strong, nonatomic) NSMutableData* thedata;
#property (strong, nonatomic) NSString* urlString;
#property (strong, nonatomic) NSURL* theUrl;
#property (strong,nonatomic) id<LokalModelProtocol>delegate;
+(void)parseJson:(NSData*)data;
#end
id<LokalModelProtocol>delegate;
#implementation LokalModel;
#synthesize thedata,urlString,theUrl,delegate;
-(void)downloadItems{
NSURL *theUrl = nil;
static NSString* urlString = #"https://balalatet.com/wp-json/wp/v2/posts";
theUrl=[NSURL URLWithString:urlString];
NSURLSession *currentSession= [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionDataTask *task = [currentSession dataTaskWithURL:theUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error){
[NSException raise:#"error" format:#"%#",error.localizedDescription];
NSLog(#"error1");
}
else{
NSLog(#"success");
[LokalModel parseJson:data];
}
}];
[task resume];
}
+(void)parseJson:(NSData*)data{
NSArray *jsonResults = [[NSArray alloc]init];
NSError *jsonerror;
jsonResults =[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonerror];
if (jsonerror)
[NSException raise:#"json error" format:#"%#",jsonerror.localizedDescription];
NSMutableArray *posts = [[NSMutableArray alloc] init];
NSMutableDictionary *jsonElenent =[NSMutableDictionary dictionary];
for (NSMutableDictionary *d in jsonResults)
{
jsonElenent=d;
PostModel *thePost=[[PostModel alloc]init];
thePost.postId= jsonElenent[#"id"];
thePost.postDate= jsonElenent[#"date"];
thePost.postDategmt= jsonElenent[#"date_gmt"];
thePost.postGuid= jsonElenent[#"guid"];
thePost.postSlug= jsonElenent[#"slug"];
thePost.postStatus= jsonElenent[#"status"];
thePost.postSticky= jsonElenent[#"sticky"];
thePost.postPingStatus= jsonElenent[#"ping_status"];
thePost.postType= jsonElenent[#"type"];
thePost.postCommentStatus= jsonElenent[#"comment_status"];
thePost.postTags= jsonElenent[#"tags"];
thePost.postTitle= jsonElenent[#"title"];
thePost.postTemplate= jsonElenent[#"template"];
thePost.postLink= jsonElenent[#"link"];
thePost.postMeta= jsonElenent[#"meta"];
thePost.postModified= jsonElenent[#"modified"];
thePost.postModifiedgmt= jsonElenent[#"modified_gmt"];
thePost.postFeaturedMedia= jsonElenent[#"featured_media"];
thePost.postFormat= jsonElenent[#"format"];
thePost.postLinks= jsonElenent[#"links"];
thePost.postAuthor= jsonElenent[#"author"];
thePost.postContent= jsonElenent[#"content"];
thePost.postCategory= jsonElenent[#"category"];
thePost.postExcerpt= jsonElenent[#"excerpt"];
NSLog(#"%#", thePost.postTitle);
[posts addObject:thePost];
}
dispatch_async(dispatch_get_main_queue(), ^{
[delegate itemsDownloaded:posts];
});
}
#end
Update
my apologies as part of my debugging info is incorrect. the nslog output did not come from the cellForRowAtIndexPath method. in fact the arr array remains empty because the
(void)itemsDownloaded:(NSMutableArray *)items
is never called. im sure i setup the protocol correctly. any thoughts on why the MainTableViewControllerClass cant get the data?
update
so i realized that i forgot to remove the line
id<LokalModelProtocol>delegate;
that i put right before the #implementation in LokalModel. but now doing so causes an error "unrecognized selector sent to instance" at the line
[delegate itemsDownloaded:posts];
I aslo tried
[self.delegate itemsDownloaded:posts];
but it throws the same exception.
Solved
My protocol method had to be an instance method, and i had it set as a class method.
Before return your cell add try to add this code in cellForRowIndexPath
[cell layoutIfneeded];
I believe you have to add a registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: prior to using dequeueReusableCellWithIdentifier:forIndexPath: (in viewDidLoad for example)
From the documentation: https://developer.apple.com/documentation/uikit/uitableview/1614878-dequeuereusablecellwithidentifie?language=objc
Important
You must register a class or nib file using the registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: method before calling this method.

Can't retrieve data after storing to disk using NSKeyedArchiver

I'm storing a custom object in a dictionary which i'm then saving to disk using NSKeyedArchiver. In the same method where I save the data, I do a quick test at the end to make sure I can load the data but everything comes out null. Why is this happening? I've been following the instructions in this tutorial: http://cocoadevcentral.com/articles/000084.php
/** Interface of viewcontroller**/
#import "User.h"
#interface BWViewController : UIViewController
{
IBOutlet UITextField *userNameField;
IBOutlet UITextField *passwordField;
IBOutlet UILabel * loginStatus;
}
#property (nonatomic, copy) NSString *holdPassword, *holdUserName;
-(IBAction)signUpButton;
-(IBAction)loginButton;
-(NSString*) pathForDataFile;
-(void) saveDataToDisk:(User*) someUser;
-(User *) loadDataFromDisk:(NSString*) theKey;
#end
/**Implementation file **/
#import "BWViewController.h"
#interface BWViewController ()
#end
#implementation BWViewController
#synthesize holdPassword,holdUserName;
-(IBAction)signUpButton
{
User * firstUser = [[User alloc] init];
firstUser.userName = userNameField.text;
firstUser.password = passwordField.text;
/**To save to file**/
[self saveDataToDisk:firstUser];
loginStatus.text = #"Thanks for signing up";
}
-(NSString*) pathForDataFile
{
NSFileManager * fileManager = [NSFileManager defaultManager];
NSString *folder =#"~/Library/Application Support/signUpApp/";
folder = [folder stringByExpandingTildeInPath];
if([fileManager fileExistsAtPath:folder] == NO)
{
[fileManager createFileAtPath:folder contents:nil attributes:nil];
}
NSString *fileName = #"signUpApp.demo";
return [folder stringByAppendingPathComponent:fileName];
}
-(void) saveDataToDisk:(User*) someUser
{
User * savingUser = someUser;
NSString * path = [self pathForDataFile];
NSMutableDictionary * rootObject = [NSMutableDictionary dictionary];
[rootObject setValue:savingUser forKey:userNameField.text];
[NSKeyedArchiver archiveRootObject:rootObject toFile:path];
NSLog(#"saving user to disk %#", savingUser.userName);
/**Testing the load process**/
User * testLoadUser;
NSMutableDictionary *unRootObject;
unRootObject = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
testLoadUser = [unRootObject valueForKey:userNameField.text];
NSLog(#"Testing the unarchive %# and %#", testLoadUser.userName, testLoadUser.password);
}
/**User implementation file**/
#import "User.h"
#implementation User
#synthesize userName, password;
- (void) encodeWithCoder: (NSCoder *)coder
{
[coder encodeObject: userName forKey:#"username"];
[coder encodeObject: password forKey:#"password"];
}
- (id) initWithCoder: (NSCoder *)coder
{
if (self = [super init])
{
self.userName = [coder decodeObjectForKey:#"username"];
self.password = [coder decodeObjectForKey:#"password"];
}
return self;
}
#end
This looks like iOS code, but you're using a Mac OS X convention for determining the folder for the path. In iOS, you should use the Documents folder.
For iOS, the path for the filename in the Documents folder should be:
- (NSString*) pathForDataFile
{
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *path = [documentsPath stringByAppendingString:#"signUpApp.demo"];
return path;
}
I'd also suggest checking the return code from archiveRootObject method, to confirm whether the archive succeeded or not. No point in going further if that failed.
Also, in your old pathForDataFile, you're calling createFileAtPath:contents:attributes:, but I suspect you intended createDirectoryAtURL:withIntermediateDirectories:attributes:error:. The former is for creating a file, and the latter is for creating a folder. And your code is trying to create a folder.
Quick things you can check..
1) IS file is Actually created on the location ?
2) Is there any data (random chars) in that file ?
3) Is your code contains <NSCopying> protocol in your user.h class?
4) Make sure you don't have - (id) initWithCoder: (NSCoder *)coder and - (void) encodeWithCoder: (NSCoder *)coder in your header file.

Storing custom objects in NSUserDefaults

I am trying to save an object in NSUserDefaults but i have not been successful in it. Scenario is that, I have an object syncObject of class SyncObjct. I have implemented following methods in SyncObject class
- (NSMutableDictionary*)toDictionary;
+ (SyncObject*)getFromDictionary :(NSMutableDictionary*)dictionary;
- (id) initWithCoder: (NSCoder *)coder;
- (void) encodeWithCoder: (NSCoder *)coder;
And also the SyncObject class is NSCoding class. Now SyncObject class contains an NSMutableArray with name jobsArray. This array contains objects of class JobsObject. I implemented same above methods for this class as well to make it coding compliant. In toDictionary method of SyncObject class, i am storing this array in dictionary by writing following line.
[dictionary setValue:jobsArray forKey:#"jobsArray"];
and I am retrieving it in getFromDictionary method of SyncOject class by writing following line.
+ (SyncObject*)getFromDictionary :(NSMutableDictionary*)dictionary
{
NSLog(#"SyncObject:getFromDictionary");
SyncObject *syncObject = [[SyncObject alloc] init];
#try
{
syncObject.noOfJobs = [[dictionary valueForKey:#"noOfJobs"] intValue];
syncObject.totalJobsPerformed = (TotalJobsPerformed*)[dictionary valueForKey:#"totalobsPerformed"];
syncObject.jobsArray = [dictionary valueForKey:#"jobsArray"];
}
#catch (NSException * e)
{
NSLog(#"EXCEPTION %#: %#", [e name], [e reason]);
}
#finally
{
}
return syncObject;
}
And also I am storing syncObject in NSUserDefault by writing following lines.
NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:syncObject];
[userStorage setObject:myEncodedObject forKey:SYNC_OBJECT];
I am retrieving the object from NSUserDefaults by writing following lines
NSData *myEncodedObject = [userStorage objectForKey: SYNC_OBJECT];
syncObject = (SyncObject*)[NSKeyedUnarchiver unarchiveObjectWithData: myEncodedObject];
Problem is that i am not able to retrieve jobsArray correctly. Some invalid object is returned and app crashes when trying to access it. Please can anyone tell me the reason of this problem?
Best Regards
I believe the problem is that NSUserDefaults does not support storing of custom object types.
From Apple's documentation when using -setObject: The value parameter can be only property list objects: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and NSDictionary objects, their contents must be property list objects.
You can review the remaining documentation directly from Apple's NSUserDefault Class Reference manual.
Custom class .h
#import <Foundation/Foundation.h>
#interface Person : NSObject
#property(nonatomic, retain) NSArray *names;
#property(strong,nonatomic)NSString *name;
#end
custom class .m
#import "Person.h"
#implementation Person
#synthesize names;
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:#"name"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super init])) {
//decode properties, other class vars
self.name = [decoder decodeObjectForKey:#"name"];
}
return self;
}
#end
To create Person class object and store and retrieve your stored data from NSUserDefaults
Stored your object :
#import "ViewController.h"
#import "Person.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextField *textBox;
#end
NSMutableArray *arr;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)saveButton:(id)sender {
Person *person =[Person new];
person.name = _textBox.text;
arr = [[NSMutableArray alloc]init];
[arr addObject:person];
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:arr];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:encodedObject forKey:#"name"];
[defaults synchronize];
}
- (IBAction)showButton:(id)sender {
_label.text = #"";
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *storedEncodedObject = [defaults objectForKey:#"name"];
NSArray *arrStoreObject = [NSKeyedUnarchiver unarchiveObjectWithData:storedEncodedObject];
for (int i=0; i<arrStoreObject.count; i++)
{
Person *storedObject = [arrStoreObject objectAtIndex:i];
_label.text = [NSString stringWithFormat:#"%# %#", _label.text , storedObject.name];
}
}
#end

objective-c multi-dimensional array

I want to use a variable-sized multi-dimensional array in my app to save data. The data structure I want to use is as below, the first element in each row is followed by corresponding multiple values.
array = { {a, a_val1, a_val2, a_val3}.
{b, b_val1},
{c, c_val1, c_val2, c_val3, c_val4, c_val5}
}
Any idea how I can implement in objective-c?
use NSMutableArray like so
NSMutableArray *curRow; /* use to access the row while loading with objects */
NSMutableArray *array = [[NSMutableArray alloc] init]; /* your main multidim array */
curRow = [NSMutableArray array];
[curRow addObject:/* what you want here */];
[curRow addObject:/* what you want here */];
[curRow addObject:/* what you want here */];
[array addObject:curRow]; /* first row is added */
/* rinse and repeat */
curRow = [NSMutableArray array];
[curRow addObject:/* what you want here */];
[curRow addObject:/* what you want here */];
[curRow addObject:/* what you want here */];
[array addObject:curRow];
use NSMutableArray
Below is the example for your understanding ...
NSMutableArray * multiArray = [[NSMutableArray alloc] initWithCapacity:5];
[multiArray addObject:[NSArray arrayWithObjects:a,a_val1,a_val2]];
[multiArray addObject:[NSArray arrayWithObjects:a,a_val1,a_val2,a_val3,a_val4]];
[multiArray addObject:[NSArray arrayWithObjects:a,a_val1,a_val5]];
[multiArray addObject:[NSArray arrayWithObjects:a,a_val1,a_val2,a_val3,a_val4,a_val5,a_val6]];
And Don't forget to release to multiArray array because we have alloced it ...
Objective-C does not have a real 2 dimensional array type but you can implement it with the
following codes..
in your header file --- yourheader.h
#import <Foundation/Foundation.h>
#interface yourheader : NSObject{
NSMutableDictionary* DictionaryArrayType;
NSMutableArray* MultiArrayType;
NSArray* CaptionTitle;
NSArray* ObjectValue;
}
#property (strong, nonatomic) NSMutableDictionary* DictionaryArrayType;
#property (strong, nonatomic) NSArray* CaptionTitle;
#property (strong, nonatomic) NSArray* ObjectValue;
#property (strong, nonatomic) NSMutableArray* MultiArrayType;
-(id) AddArrayObjects:(NSString*)_Name : (NSString*)_Surname :(NSString*)_Age;
-(id) AddArrayDictionaryObject:(NSArray*)_ArrayObject : (NSArray*)_ArrayKey;
-(id) AddMultiArrayType:(id)_ArrayObject;
-(void) ShowMultiArrayType:(id)_ArrayObject;
#end
Now add to your objective-c file ---- objective-c.m
#import "yourheader.h"
#implimentation yourheader
#synthesize DictionaryArrayType;
#synthesize CaptionTitle;
#synthesize ObjectValue;
#synthesize MultiArrayType;
-(id)init {
if(self = [super init]){
NSString* const NAME = #"NAME";
NSString* const SURNAME = #"SURNAME";
NSString* const AGE = #"AGE";
//Adding fixed content to CaptionTitle Array
[self setCaptionTitle:[NSArray arrayWithObjects:NAME, SURNAME, AGE, nil]];
//add values to ObjectValue array
[self AddArrayObjects:#"Bob" :#"Obi" :#"200"];
//add values to dictionary
[self AddDictionaryArrayType:ObjectValue :CaptionTitle];
//Add to the Multi dimensional array [][]
[self AddMultiArrayType:DictionaryArrayType];
//add the second row values to ObjectValue array
[self AddArrayObjects:#"Barack" :#"Obama" :#"50"];
//add values to dictionary
[self AddDictionaryArrayType:ObjectValue :CaptionTitle];
//Add to the Multi dimensional array [][]
[self AddMultiArrayType:DictionaryArrayType];
//display the 2d Array
[self ShowMultiArrayType:MultiArrayType];
}
return self;
}
-(id)AddArrayObjects:(NSString *)_name :(NSString *)_surname :(NSString *)_age {
//Set the Array Objects;
[self setObjectValue:[NSArray arrayWithObjects:_name, _surname, _age, nil]];
return self;
}
-(id)AddDictionaryArrayType:(NSArray *)_ArrayObject :(NSArray*)_ArrayKey {
if(!DictionaryArrayType) {
//initialize disctionary
[self setDictionaryArrayType:[NSMutableDictionary dictionary]];
}
//add array obeject and Fixed Key decleared in CaptionTitle array
DictionaryArrayType = [NSMutableDictionary dictionaryWithObjects:_ArrayObject forKeys:_ArrayKey];
return self;
}
-(id) AddMultiArrayType:(id)_ArrayObject {
if(!MultiArrayType) {
[self setMultiArrayType:[NSMutableArray array]];
}
[MultiArrayType addObject:_ArrayObject];
return self;
}
-(void)ShowMultiArrayType:_ArrayObject {
for(id objects in _ArrayObject ) {
for(id key in objects) {
NSLog(#"%# key = : object = %#", key, [objects objectForKey:key]);
}
}
}
#end;
To finish add this to your appdelegate.m file inside the app
yourclassname* _yourclasspointer = [[yourclassname alloc] init];
[_youclasspointer ShowMultiArrayType:[_yourclasspointer MultiArrayType]];
You should see it in you console.

Issue with save file to Documents NSMutableArray with NSObjects

I'm a newbie iOS developer.
I wrote a small application that save an NSMutableArray array with my objects that derived from NSObject.
Application do the save but the file isn't created in document directory and application can't read.
this issue is both on the simulator and my iPhone 3gs 4.2.1
My NSMutableArray definition inside the appDelegate class:
#property (nonatomic,retain, readwrite) NSMutableArray *places;
My NSObject class:
#import <Foundation/Foundation.h>
#interface Place : NSObject {
NSString *name;
NSString *location;
}
-(id) init:(NSString *)name: (NSString *)location;
#property (retain,nonatomic,readwrite) NSString *name;
#property (retain,nonatomic,readwrite) NSString *location;
#end
My StorageService library class:
#import "StorageService.h"
#implementation StorageService
-(id) init {
self = [super init];
if (self != nil) {
}
return self;
}
-(void) saveArrayToFile:(NSString*) filename : (NSMutableArray *)arrayToSave{
// get full path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *fullPath = [paths objectAtIndex:0];
fullPath = [fullPath stringByAppendingPathComponent:filename];
NSLog(#"Save in %#",fullPath);
[arrayToSave writeToFile:fullPath atomically:YES];
}
-(NSMutableArray*) readArrayFromFile:(NSString *)filename {
// get full path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *fullPath = [paths objectAtIndex:0];
fullPath = [fullPath stringByAppendingPathComponent:filename];
if ([[NSFileManager defaultManager] fileExistsAtPath:fullPath]) {
NSMutableArray *data = [[NSMutableArray alloc] initWithContentsOfFile:fullPath];
if (data == nil) {
data = [[NSMutableArray alloc] init];
}
NSLog(#"Read from %#",fullPath);
return data;
} else {
NSMutableArray *data = [[NSMutableArray alloc] initWithContentsOfFile:fullPath];
return data;
}
}
-(void) dealloc {
[super dealloc];
}
#end
and My functions in the appDelegate:
-(void) saveApplicationData {
[self.storageService saveArrayToFile : PLACES_FILE : self.places];
}
-(void) loadApplicationData {
self.places = [self.storageService readArrayFromFile:PLACES_FILE];
}
Here is my class that holds constant to filename:
#import <Foundation/Foundation.h>
extern NSString * const PLACES_FILE = #"Places.dat";
#interface ApplicationConstants : NSObject {
}
#end
So what is wrong?
Thank you guys.
What you want is to let Place conform to the NSCoding protocol, to allow for serialization to and from files (and in memory data if wanted)
Extend Place as (I have also changed the name of the init method as your name was against every naming practice iOS has):
#interface Place : NSObject <NSCoding> {
NSString *name;
NSString *location;
}
-(id)initWithName:(NSString *)name location:(NSString *)location;
#property (retain,nonatomic,readwrite) NSString *name;
#property (retain,nonatomic,readwrite) NSString *location;
#end
Your implementation is quite simple but you also need to implement two methods defined by the NSCoding protocol:
#implementation Place
#synthesize name, location;
-(id)initWithName:(NSString *)aName location:(NSString *)aLocation {
self = [super init];
if (self) {
self.name = aName;
self.location = aLocation;
}
return self;
}
-(id)initWithWithCoder:(NSCoder)decoder {
self = [super initWithCoder:decoder];
if (self) {
self.name = [decoder decodeObjectForKey:#"name"];
self.location = [decoder decodeObjectForKey:#"location";
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeObject:self.location forKey:#"location"];
[super encodeWithCoder:encoder];
}
#end
With this in place, saving the places array to disk is as easy as:
[NSKeyedArchiver archiveRootObject:places toFile:path];
And decoding just as easy:
places = [[KSKeyUnarchiver unarchiveObjectWithFile:path] retain];
To use writeToFile objects in array need to be plist capable type (NSDate, NSDate, NSString, NSArray, NSDictionary)
Implement NSCoding on the objects in array and use NSKeyedArchiver to serialize/deserialize.
write:
[NSKeyedArchiver archiveRootObject:myArray toFile:self.places];
read:
[NSKeyedUnarchiver unarchiveObjectWithFile:path];
More info can be found here:
Persisting Custom Objects